From e9ea7b34e305167fd227fdb136762150319aacf9 Mon Sep 17 00:00:00 2001 From: davidt0x Date: Thu, 19 Mar 2020 12:00:31 -0400 Subject: [PATCH 001/285] Re-enable dask distributed. --- .../predator_prey_opt/predator_prey_dmt.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Scripts/Debug/predator_prey_opt/predator_prey_dmt.py b/Scripts/Debug/predator_prey_opt/predator_prey_dmt.py index 2933a3c80d1..bce04b245d3 100644 --- a/Scripts/Debug/predator_prey_opt/predator_prey_dmt.py +++ b/Scripts/Debug/predator_prey_opt/predator_prey_dmt.py @@ -417,21 +417,20 @@ def run_search(): import joblib import hypertunity as ht - #client = Client(scheduler_file='scheduler.json') - client = Client() + client = Client(scheduler_file='scheduler.json') + #client = Client() # Setup local cluster print(client) domain = ht.Domain({ "cost_rate": set([-.8]) }) - # with joblib.parallel_backend('dask'): - # with joblib.Parallel() as parallel: - # print("Doing the work ... ") - # results = parallel(joblib.delayed(run_games)(*domain.sample().as_namedtuple()) for s in range(1)) - # - # print(results) - run_games(-.8) + with joblib.parallel_backend('dask'): + with joblib.Parallel() as parallel: + print("Doing the work ... ") + results = parallel(joblib.delayed(run_games)(*domain.sample().as_namedtuple()) for s in range(1)) + + print(results) if __name__ == "__main__": run_search() From a668099f9e219c6acb5982c1677f6def411ad6a2 Mon Sep 17 00:00:00 2001 From: davidt0x Date: Wed, 20 May 2020 13:54:51 -0400 Subject: [PATCH 002/285] Merge branch 'devel' of https://github.com/PrincetonUniversity/PsyNeuLink into model-fit # Conflicts: # psyneulink/components/functions/function.py # psyneulink/components/mechanisms/mechanism.py # psyneulink/components/system.py # psyneulink/globals/environment.py # psyneulink/library/subsystems/evc/evcauxiliary.py --- .../hackathon/stability_flexibility_csi.py | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 Scripts/Debug/hackathon/stability_flexibility_csi.py diff --git a/Scripts/Debug/hackathon/stability_flexibility_csi.py b/Scripts/Debug/hackathon/stability_flexibility_csi.py new file mode 100644 index 00000000000..f08264418fe --- /dev/null +++ b/Scripts/Debug/hackathon/stability_flexibility_csi.py @@ -0,0 +1,249 @@ +import psyneulink as pnl +import psyneulink.core.llvm as pnlvm + +import numpy as np +import random +import pytest +import pandas as pd + +# Define function to generate a counterbalanced trial sequence with a specified switch trial frequency +def generateTrialSequence(N, Frequency): + + # Compute trial number + nTotalTrials = N + switchFrequency = Frequency + + nSwitchTrials = int(nTotalTrials * switchFrequency) + nRepeatTrials = int(nTotalTrials - nSwitchTrials) + + # Determine task transitions + transitions = [1] * nSwitchTrials + [0] * nRepeatTrials + order = np.random.permutation(list(range(nTotalTrials))) + transitions[:] = [transitions[i] for i in order] + + # Determine stimuli with 50% congruent trials + stimuli = [[1, 1]] * int(nSwitchTrials/4) + [[1, -1]] * int(nSwitchTrials/4) + [[-1, -1]] * int(nSwitchTrials/4) + [[-1, 1]] * int(nSwitchTrials/4) + \ + [[1, 1]] * int(nRepeatTrials/4) + [[1, -1]] * int(nRepeatTrials/4) + [[-1, -1]] * int(nRepeatTrials/4) + [[-1, 1]] * int(nRepeatTrials/4) + stimuli[:] = [stimuli[i] for i in order] + + # Determine cue-stimulus intervals + CSI = [60] * int(nSwitchTrials / 8) + [80] * int(nSwitchTrials / 8) + \ + [60] * int(nSwitchTrials / 8) + [80] * int(nSwitchTrials / 8) + \ + [60] * int(nSwitchTrials / 8) + [80] * int(nSwitchTrials / 8) + \ + [60] * int(nSwitchTrials / 8) + [80] * int(nSwitchTrials / 8) + \ + [60] * int(nRepeatTrials / 8) + [80] * int(nRepeatTrials / 8) + \ + [60] * int(nRepeatTrials / 8) + [80] * int(nRepeatTrials / 8) + \ + [60] * int(nRepeatTrials / 8) + [80] * int(nRepeatTrials / 8) + \ + [60] * int(nRepeatTrials / 8) + [80] * int(nRepeatTrials / 8) + CSI[:] = [CSI[i] for i in order] + + # Set the task order + tasks = [[1, 0]] * (nTotalTrials + 1) + for i in list(range(nTotalTrials)): + if transitions[i] == 0: + tasks[i + 1] = tasks[i] + if transitions[i] == 1: + if tasks[i] == [1, 0]: + tasks[i + 1] = [0, 1] + if tasks[i] == [0, 1]: + tasks[i + 1] = [1, 0] + tasks = tasks[1:] + + # # Check whether combinations of transitions, stimuli and CSIs are counterbalanced + + # # This is used later to check whether trials are counterbalanced + # stimuli_type = [1] * int(nSwitchTrials/4) + [2] * int(nSwitchTrials/4) + [3] * int(nSwitchTrials/4) + [4] * int(nSwitchTrials/4) + \ + # [1] * int(nRepeatTrials/4) + [2] * int(nRepeatTrials/4) + [3] * int(nRepeatTrials/4) + [4] * int(nRepeatTrials/4) + # stimuli_type[:] = [stimuli_type[i] for i in order] + + # Trials = pd.DataFrame({'TrialType': transitions, + # 'Stimuli': stimuli_type, + # 'CSI': CSI + # }, columns= ['TrialType', 'Stimuli', 'CSI']) + # + # trial_counts = Trials.pivot_table(index=['TrialType', 'Stimuli', 'CSI'], aggfunc='size') + # print (trial_counts) + + return tasks, stimuli, CSI + + +# Stability-Flexibility Model + +GAIN = 1 +LEAK = 1 +COMP = 7.0 + +AUTOMATICITY = .15 # Automaticity Weight + +STARTING_POINT = 0.0 # Starting Point +THRESHOLD = 1 # Threshold +NOISE = 0.025 # Noise +SCALE = .05 # Scales DDM inputs so threshold can be set to 1 + +# Task Layer: [Color, Motion] {0, 1} Mutually Exclusive +# Origin Node +taskLayer = pnl.TransferMechanism(size=2, + function=pnl.Linear(slope=1, intercept=0), + output_ports=[pnl.RESULT], + name='Task Input [I1, I2]') + +# Stimulus Layer: [Color Stimulus, Motion Stimulus] +# Origin Node +stimulusInfo = pnl.TransferMechanism(size=2, + function=pnl.Linear(slope=1, intercept=0), + output_ports=[pnl.RESULT], + name="Stimulus Input [S1, S2]") + +# Cue-To-Stimulus Interval Layer +# Origin Node +cueInterval = pnl.TransferMechanism(size=1, + function=pnl.Linear(slope=1, intercept=0), + output_ports=[pnl.RESULT], + name='Cue-Stimulus Interval') + +# Control Module Layer: [Color Activation, Motion Activation] +controlModule = pnl.LCAMechanism(size=2, + function=pnl.Logistic(gain=GAIN), + leak=LEAK, + competition=COMP, + noise=0, + termination_measure=pnl.TimeScale.TRIAL, + termination_threshold=10, + time_step_size=.001, + name='Task Activations [Act1, Act2]') + +# Control Mechanism Setting Cue-To-Stimulus Interval +csiController = pnl.ControlMechanism(monitor_for_control=cueInterval, + control_signals=[(pnl.TERMINATION_THRESHOLD, controlModule)], + modulation=pnl.OVERRIDE) + +# Hadamard product of controlModule and Stimulus Information +nonAutomaticComponent = pnl.TransferMechanism(size=2, + function=pnl.Linear(slope=1, intercept=0), + input_ports=pnl.InputPort(combine=pnl.PRODUCT), + output_ports=[pnl.RESULT], + name='Non-Automatic Component [S1*Act1, S2*Act2]') + +# Multiply Stimulus Input by the automaticity weight +congruenceWeighting = pnl.TransferMechanism(size=2, + function=pnl.Linear(slope=AUTOMATICITY, intercept=0), + output_ports=[pnl.RESULT], + name="Automaticity-weighted Stimulus Input [w*S1, w*S2]") + +# Summation of nonAutomatic and Automatic Components +ddmCombination = pnl.TransferMechanism(size=1, + function=pnl.Linear(slope=1, intercept=0), + input_ports=pnl.InputPort(combine=pnl.SUM), + output_ports=[pnl.RESULT], + name="Drift = (w*S1 + w*S2) + (S1*Act1 + S2*Act2)") + +# Scale DDM inputs to smaller value +ddmInputScale = pnl.TransferMechanism(size=1, + function=pnl.Linear(slope=SCALE, intercept=0), + output_ports=[pnl.RESULT], + name='Scaled DDM Input') + +# Decision Module +decisionMaker = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_point=STARTING_POINT, + threshold=THRESHOLD, + noise=NOISE), + reinitialize_when=pnl.AtTrialStart(), + output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], + name='DDM') + +taskLayer.set_log_conditions([pnl.RESULT]) +stimulusInfo.set_log_conditions([pnl.RESULT]) +controlModule.set_log_conditions([pnl.RESULT, 'termination_threshold']) +nonAutomaticComponent.set_log_conditions([pnl.RESULT]) +congruenceWeighting.set_log_conditions([pnl.RESULT]) +ddmCombination.set_log_conditions([pnl.RESULT]) +ddmInputScale.set_log_conditions([pnl.RESULT]) +decisionMaker.set_log_conditions([pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME]) + +# Composition Creation +stabilityFlexibility = pnl.Composition() + +# Node Creation +stabilityFlexibility.add_node(taskLayer) +stabilityFlexibility.add_node(stimulusInfo) +stabilityFlexibility.add_node(cueInterval) +stabilityFlexibility.add_node(controlModule) +stabilityFlexibility.add_node(csiController) +stabilityFlexibility.add_node(nonAutomaticComponent) +stabilityFlexibility.add_node(congruenceWeighting) +stabilityFlexibility.add_node(ddmCombination) +stabilityFlexibility.add_node(ddmInputScale) +stabilityFlexibility.add_node(decisionMaker) + +# Projection Creation +stabilityFlexibility.add_projection(sender=taskLayer, receiver=controlModule) +stabilityFlexibility.add_projection(sender=controlModule, receiver=nonAutomaticComponent) +stabilityFlexibility.add_projection(sender=stimulusInfo, receiver=nonAutomaticComponent) +stabilityFlexibility.add_projection(sender=stimulusInfo, receiver=congruenceWeighting) +stabilityFlexibility.add_projection(sender=nonAutomaticComponent, receiver=ddmCombination) +stabilityFlexibility.add_projection(sender=congruenceWeighting, receiver=ddmCombination) +stabilityFlexibility.add_projection(sender=ddmCombination, receiver=ddmInputScale) +stabilityFlexibility.add_projection(sender=ddmInputScale, receiver=decisionMaker) + + + +# Hot-fix currently necessary to allow control module and DDM to execute in parallel in compiled mode +# We need two gates in order to output both values (decision and response) from the ddm +decisionGate = pnl.ProcessingMechanism(size=1, name="DECISION_GATE") +stabilityFlexibility.add_node(decisionGate) + +responseGate = pnl.ProcessingMechanism(size=1, name="RESPONSE_GATE") +stabilityFlexibility.add_node(responseGate) + +stabilityFlexibility.add_projection(sender=decisionMaker.output_ports[0], receiver=decisionGate) +stabilityFlexibility.add_projection(sender=decisionMaker.output_ports[1], receiver=responseGate) + +# Sets scheduler conditions, so that the gates are not executed (and hence the composition doesn't finish) until decisionMaker is finished +stabilityFlexibility.scheduler.add_condition(decisionGate, pnl.WhenFinished(decisionMaker)) +stabilityFlexibility.scheduler.add_condition(responseGate, pnl.WhenFinished(decisionMaker)) + + + +# Origin Node Inputs +USE_GPU = True + +taskTrain, stimulusTrain, cueTrain = generateTrialSequence(80, 0.5) + +inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain, cueInterval: cueTrain} + +if not USE_GPU: + stabilityFlexibility.run(inputs, bin_execute=True) + res = stabilityFlexibility.results +else: + stabilityFlexibility._analyze_graph() + num_executions = 10 + var = [inputs for _ in range(num_executions)] + e = pnlvm.execution.CompExecution(stabilityFlexibility, [None for _ in range(num_executions)]) + res = e.cuda_run(var, 1, 1) + +# mechanisms +# A = pnl.ProcessingMechanism(name="A", +# function=pnl.AdaptiveIntegrator(rate=0.1)) +# B = pnl.ProcessingMechanism(name="B", +# function=pnl.Logistic) +# +# comp = pnl.Composition(name="comp") +# comp.add_linear_processing_pathway([A, B]) +# comp._analyze_graph() +# var = {A: [[[2.0]], [[3.0]]]} +# var = [var for _ in range(num_executions)] +# e = pnlvm.execution.CompExecution(comp, [None for _ in range(num_executions)]) +# res = e.cuda_run(var, 4, 2) +# print(np.array(res)) + +#taskLayer.log.print_entries() +#stimulusInfo.log.print_entries() +#controlModule.log.print_entries() +#nonAutomaticComponent.log.print_entries() +#congruenceWeighting.log.print_entries() +#ddmCombination.log.print_entries() +#ddmInputScale.log.print_entries() +#decisionMaker.log.print_entries() +# +# for trial in stabilityFlexibility.results: +# print(trial) From 6a2881e27781734c2ee3de6fbefa918db0285429 Mon Sep 17 00:00:00 2001 From: davidt0x Date: Mon, 13 Jul 2020 11:04:18 -0400 Subject: [PATCH 003/285] Hack to fix LCA leak --- .../hackathon/stability_flexibility_csi.py | 38 ++++++++++--------- .../processing/transfermechanism.py | 3 +- .../processing/transfer/lcamechanism.py | 6 ++- .../transfer/recurrenttransfermechanism.py | 3 +- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Scripts/Debug/hackathon/stability_flexibility_csi.py b/Scripts/Debug/hackathon/stability_flexibility_csi.py index f08264418fe..68aa57a68e9 100644 --- a/Scripts/Debug/hackathon/stability_flexibility_csi.py +++ b/Scripts/Debug/hackathon/stability_flexibility_csi.py @@ -71,7 +71,7 @@ def generateTrialSequence(N, Frequency): GAIN = 1 LEAK = 1 -COMP = 7.0 +COMP = 7.5 AUTOMATICITY = .15 # Automaticity Weight @@ -109,7 +109,7 @@ def generateTrialSequence(N, Frequency): noise=0, termination_measure=pnl.TimeScale.TRIAL, termination_threshold=10, - time_step_size=.001, + time_step_size=.1, name='Task Activations [Act1, Act2]') # Control Mechanism Setting Cue-To-Stimulus Interval @@ -147,7 +147,7 @@ def generateTrialSequence(N, Frequency): decisionMaker = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_point=STARTING_POINT, threshold=THRESHOLD, noise=NOISE), - reinitialize_when=pnl.AtTrialStart(), + reset_stateful_function_when=pnl.AtTrialStart(), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], name='DDM') @@ -202,24 +202,26 @@ def generateTrialSequence(N, Frequency): stabilityFlexibility.scheduler.add_condition(decisionGate, pnl.WhenFinished(decisionMaker)) stabilityFlexibility.scheduler.add_condition(responseGate, pnl.WhenFinished(decisionMaker)) - - # Origin Node Inputs -USE_GPU = True +USE_GPU = False taskTrain, stimulusTrain, cueTrain = generateTrialSequence(80, 0.5) -inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain, cueInterval: cueTrain} +inputs = {taskLayer: taskTrain[0:10], stimulusInfo: stimulusTrain[0:10], cueInterval: cueTrain[0:10]} + +#stabilityFlexibility.show_graph() if not USE_GPU: - stabilityFlexibility.run(inputs, bin_execute=True) + stabilityFlexibility.run(inputs, bin_execute=False) res = stabilityFlexibility.results else: stabilityFlexibility._analyze_graph() - num_executions = 10 + num_executions = 1000 var = [inputs for _ in range(num_executions)] + adjusted_inputs, num_input_sets = stabilityFlexibility._parse_run_inputs(inputs) + var = [adjusted_inputs for _ in range(num_executions)] e = pnlvm.execution.CompExecution(stabilityFlexibility, [None for _ in range(num_executions)]) - res = e.cuda_run(var, 1, 1) + res = e.cuda_run(var, 1, num_input_sets) # mechanisms # A = pnl.ProcessingMechanism(name="A", @@ -236,14 +238,14 @@ def generateTrialSequence(N, Frequency): # res = e.cuda_run(var, 4, 2) # print(np.array(res)) -#taskLayer.log.print_entries() -#stimulusInfo.log.print_entries() -#controlModule.log.print_entries() -#nonAutomaticComponent.log.print_entries() -#congruenceWeighting.log.print_entries() -#ddmCombination.log.print_entries() -#ddmInputScale.log.print_entries() -#decisionMaker.log.print_entries() +# taskLayer.log.print_entries() +# stimulusInfo.log.print_entries() +controlModule.log.print_entries() +# nonAutomaticComponent.log.print_entries() +# congruenceWeighting.log.print_entries() +# ddmCombination.log.print_entries() +# ddmInputScale.log.print_entries() +# decisionMaker.log.print_entries() # # for trial in stabilityFlexibility.results: # print(trial) diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index ca3ae2adbef..ef1fd91055f 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -1113,7 +1113,8 @@ def __init__(self, integrator_mode=False, integrator_function=None, initial_value=None, - integration_rate=0.5, + #integration_rate=0.5, + integration_rate = None, on_resume_integrator_mode=INSTANTANEOUS_MODE_VALUE, noise=0.0, clip=None, diff --git a/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py index e0e22a5effe..04289cb416b 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py @@ -391,7 +391,8 @@ class Parameters(RecurrentTransferMechanism.Parameters): function = Parameter(Logistic, stateful=False, loggable=False) - leak = Parameter(0.5, modulable=True) + #leak = Parameter(0.5, modulable=True) + integration_rate = Parameter(0.5, modulable=True, aliases='leak') auto = Parameter(0.0, modulable=True, aliases='self_excitation') hetero = Parameter(-1.0, modulable=True) competition = Parameter(1.0, modulable=True) @@ -563,7 +564,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) + #leak = self._get_current_mechanism_param("leak", context) + leak = self._get_current_mechanism_param("integration_rate", context) time_step_size = self._get_current_mechanism_param("time_step_size", context) # if not self.integrator_function: diff --git a/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py index 475a14ec3c4..2b735483d31 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py @@ -656,7 +656,8 @@ def __init__(self, integrator_mode=False, integrator_function=None, initial_value=None, - integration_rate: is_numeric_or_none=0.5, + #integration_rate: is_numeric_or_none=0.5, + integration_rate: is_numeric_or_none = None, noise=0.0, clip=None, enable_learning:bool=False, From e23de9e8c6160d3c17e235a5ff744c597c9863eb Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 6 Aug 2020 18:32:32 -0400 Subject: [PATCH 004/285] ChangE PARAMetrs in stability_flexibility_csi --- .../hackathon/stability_flexibility_csi.py | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/Scripts/Debug/hackathon/stability_flexibility_csi.py b/Scripts/Debug/hackathon/stability_flexibility_csi.py index 68aa57a68e9..51eb795de33 100644 --- a/Scripts/Debug/hackathon/stability_flexibility_csi.py +++ b/Scripts/Debug/hackathon/stability_flexibility_csi.py @@ -76,9 +76,9 @@ def generateTrialSequence(N, Frequency): AUTOMATICITY = .15 # Automaticity Weight STARTING_POINT = 0.0 # Starting Point -THRESHOLD = 1 # Threshold -NOISE = 0.025 # Noise -SCALE = .05 # Scales DDM inputs so threshold can be set to 1 +THRESHOLD = 0.2 # Threshold +NOISE = 0.1 # Noise +SCALE = 1 # Scales DDM inputs so threshold can be set to 1 # Task Layer: [Color, Motion] {0, 1} Mutually Exclusive # Origin Node @@ -146,14 +146,16 @@ def generateTrialSequence(N, Frequency): # Decision Module decisionMaker = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_point=STARTING_POINT, threshold=THRESHOLD, - noise=NOISE), + noise=NOISE, + time_step_size=0.001), reset_stateful_function_when=pnl.AtTrialStart(), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], name='DDM') taskLayer.set_log_conditions([pnl.RESULT]) stimulusInfo.set_log_conditions([pnl.RESULT]) -controlModule.set_log_conditions([pnl.RESULT, 'termination_threshold']) +#controlModule.set_log_conditions([pnl.RESULT, 'termination_threshold']) +controlModule.set_log_conditions([pnl.RESULT]) nonAutomaticComponent.set_log_conditions([pnl.RESULT]) congruenceWeighting.set_log_conditions([pnl.RESULT]) ddmCombination.set_log_conditions([pnl.RESULT]) @@ -207,12 +209,21 @@ def generateTrialSequence(N, Frequency): taskTrain, stimulusTrain, cueTrain = generateTrialSequence(80, 0.5) -inputs = {taskLayer: taskTrain[0:10], stimulusInfo: stimulusTrain[0:10], cueInterval: cueTrain[0:10]} +#taskTrain = np.array([[1.0, 0.0], [0., 1.], [1., 0.]]) +#stimulusTrain = np.array([[-1., 1.], [ 1., 1.], [-1., -1.]]) +#cueTrain = np.array([60., 60., 80.]) -#stabilityFlexibility.show_graph() +inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain, cueInterval: cueTrain} + +# stabilityFlexibility.show_graph() if not USE_GPU: - stabilityFlexibility.run(inputs, bin_execute=False) + stabilityFlexibility.run(inputs, bin_execute=True) + + import time + t0 = time.time() + stabilityFlexibility.run(inputs, bin_execute="LLVMRun", ) + res = stabilityFlexibility.results else: stabilityFlexibility._analyze_graph() @@ -223,29 +234,21 @@ def generateTrialSequence(N, Frequency): e = pnlvm.execution.CompExecution(stabilityFlexibility, [None for _ in range(num_executions)]) res = e.cuda_run(var, 1, num_input_sets) -# mechanisms -# A = pnl.ProcessingMechanism(name="A", -# function=pnl.AdaptiveIntegrator(rate=0.1)) -# B = pnl.ProcessingMechanism(name="B", -# function=pnl.Logistic) -# -# comp = pnl.Composition(name="comp") -# comp.add_linear_processing_pathway([A, B]) -# comp._analyze_graph() -# var = {A: [[[2.0]], [[3.0]]]} -# var = [var for _ in range(num_executions)] -# e = pnlvm.execution.CompExecution(comp, [None for _ in range(num_executions)]) -# res = e.cuda_run(var, 4, 2) -# print(np.array(res)) - # taskLayer.log.print_entries() # stimulusInfo.log.print_entries() -controlModule.log.print_entries() +# controlModule.log.print_entries() # nonAutomaticComponent.log.print_entries() # congruenceWeighting.log.print_entries() # ddmCombination.log.print_entries() # ddmInputScale.log.print_entries() # decisionMaker.log.print_entries() + +# from matplotlib import pyplot as plt +# ddm_log = ddmInputScale.log.nparray_dictionary() +# lca_log = controlModule.log.nparray_dictionary() +# plt.plot(lca_log['Composition-0']['RESULT'][:,0], lw=2, color='orange') +# plt.plot(lca_log['Composition-0']['RESULT'][:,1], lw=2, color='red') +# plt.plot(ddm_log['Composition-0']['RESULT'], lw=2, color='green') # # for trial in stabilityFlexibility.results: # print(trial) From 3856325bf672d308b3e5fb6cb25a7d82087e72be Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 15 Sep 2020 12:41:01 -0400 Subject: [PATCH 005/285] Changes to Bryant's model to get GPU compiliation --- .../hackathon/stability_flexibility_csi.py | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/Scripts/Debug/hackathon/stability_flexibility_csi.py b/Scripts/Debug/hackathon/stability_flexibility_csi.py index 51eb795de33..8de8c9a2fce 100644 --- a/Scripts/Debug/hackathon/stability_flexibility_csi.py +++ b/Scripts/Debug/hackathon/stability_flexibility_csi.py @@ -96,10 +96,10 @@ def generateTrialSequence(N, Frequency): # Cue-To-Stimulus Interval Layer # Origin Node -cueInterval = pnl.TransferMechanism(size=1, - function=pnl.Linear(slope=1, intercept=0), - output_ports=[pnl.RESULT], - name='Cue-Stimulus Interval') +# cueInterval = pnl.TransferMechanism(size=1, +# function=pnl.Linear(slope=1, intercept=0), +# output_ports=[pnl.RESULT], +# name='Cue-Stimulus Interval') # Control Module Layer: [Color Activation, Motion Activation] controlModule = pnl.LCAMechanism(size=2, @@ -108,14 +108,14 @@ def generateTrialSequence(N, Frequency): competition=COMP, noise=0, termination_measure=pnl.TimeScale.TRIAL, - termination_threshold=10, + termination_threshold=60, time_step_size=.1, name='Task Activations [Act1, Act2]') # Control Mechanism Setting Cue-To-Stimulus Interval -csiController = pnl.ControlMechanism(monitor_for_control=cueInterval, - control_signals=[(pnl.TERMINATION_THRESHOLD, controlModule)], - modulation=pnl.OVERRIDE) +# csiController = pnl.ControlMechanism(monitor_for_control=cueInterval, +# control_signals=[(pnl.TERMINATION_THRESHOLD, controlModule)], +# modulation=pnl.OVERRIDE) # Hadamard product of controlModule and Stimulus Information nonAutomaticComponent = pnl.TransferMechanism(size=2, @@ -163,14 +163,14 @@ def generateTrialSequence(N, Frequency): decisionMaker.set_log_conditions([pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME]) # Composition Creation -stabilityFlexibility = pnl.Composition() +stabilityFlexibility = pnl.Composition(controller_mode=pnl.BEFORE) # Node Creation stabilityFlexibility.add_node(taskLayer) stabilityFlexibility.add_node(stimulusInfo) -stabilityFlexibility.add_node(cueInterval) +# stabilityFlexibility.add_node(cueInterval) stabilityFlexibility.add_node(controlModule) -stabilityFlexibility.add_node(csiController) +# stabilityFlexibility.add_node(csiController) stabilityFlexibility.add_node(nonAutomaticComponent) stabilityFlexibility.add_node(congruenceWeighting) stabilityFlexibility.add_node(ddmCombination) @@ -205,15 +205,17 @@ def generateTrialSequence(N, Frequency): stabilityFlexibility.scheduler.add_condition(responseGate, pnl.WhenFinished(decisionMaker)) # Origin Node Inputs -USE_GPU = False +USE_GPU = True taskTrain, stimulusTrain, cueTrain = generateTrialSequence(80, 0.5) -#taskTrain = np.array([[1.0, 0.0], [0., 1.], [1., 0.]]) -#stimulusTrain = np.array([[-1., 1.], [ 1., 1.], [-1., -1.]]) -#cueTrain = np.array([60., 60., 80.]) +taskTrain = np.array([[1.0, 0.0], [0., 1.], [1., 0.]]) +stimulusTrain = np.array([[-1., 1.], [ 1., 1.], [-1., -1.]]) +cueTrain = np.array([60., 60., 80.]) + +# inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain, cueInterval: cueTrain} +inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain} -inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain, cueInterval: cueTrain} # stabilityFlexibility.show_graph() @@ -226,13 +228,30 @@ def generateTrialSequence(N, Frequency): res = stabilityFlexibility.results else: - stabilityFlexibility._analyze_graph() - num_executions = 1000 - var = [inputs for _ in range(num_executions)] - adjusted_inputs, num_input_sets = stabilityFlexibility._parse_run_inputs(inputs) - var = [adjusted_inputs for _ in range(num_executions)] - e = pnlvm.execution.CompExecution(stabilityFlexibility, [None for _ in range(num_executions)]) - res = e.cuda_run(var, 1, num_input_sets) + ocm_mode = "PTX" + + search_range = pnl.SampleSpec(start=1.0, stop=7.5, step=0.001) + control_signal = pnl.ControlSignal(projections=[('competition', controlModule)], + variable=1.0, + allocation_samples=search_range, + intensity_cost_function=pnl.Linear(slope=0.)) + + objective_mech = pnl.ObjectiveMechanism(monitor=[responseGate]) + ocm = pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, + features=[stimulusInfo.input_port], + objective_mechanism=objective_mech, + function=pnl.GridSearch(), + control_signals=[control_signal], + comp_execution_mode=ocm_mode) + # objective_mech.log.set_log_conditions(pnl.OUTCOME) + + stabilityFlexibility.add_controller(ocm) + + import time + + t0 = time.time() + stabilityFlexibility.run(inputs, bin_execute='Python-PTX') + print(f"Elapsed: {time.time() - t0}") # taskLayer.log.print_entries() # stimulusInfo.log.print_entries() From 562a91ea6830d77eef2d7d53df820397bd6ed646 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 3 Dec 2020 23:15:19 -0500 Subject: [PATCH 006/285] First commit of fitfunctions --- .../hackathon/stability_flexibility_csi.py | 1 + .../core/components/functions/fitfunctions.py | 321 ++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 psyneulink/core/components/functions/fitfunctions.py diff --git a/Scripts/Debug/hackathon/stability_flexibility_csi.py b/Scripts/Debug/hackathon/stability_flexibility_csi.py index 8de8c9a2fce..1693cab5005 100644 --- a/Scripts/Debug/hackathon/stability_flexibility_csi.py +++ b/Scripts/Debug/hackathon/stability_flexibility_csi.py @@ -6,6 +6,7 @@ import pytest import pandas as pd + # Define function to generate a counterbalanced trial sequence with a specified switch trial frequency def generateTrialSequence(N, Frequency): diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py new file mode 100644 index 00000000000..a6f7eb2ba23 --- /dev/null +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -0,0 +1,321 @@ +import typing +import time +import numpy as np + +from psyneulink.core.globals.context import Context +from psyneulink.core.globals.parameters import Parameter +from psyneulink.core.scheduling.condition import AtTrialStart +from psyneulink.core.globals.parameters import Parameter +from psyneulink.core.components.functions.optimizationfunctions import OBJECTIVE_FUNCTION, SEARCH_SPACE, \ + OptimizationFunction + +from fastkde import fastKDE +from scipy.interpolate import interpn +from scipy.optimize import differential_evolution + + +def simulation_likelihood(sim_data, + exp_data=None, + categorical_dims=None, + combine_trials=False): + """ + Compute the likelihood of a simulation dataset (or the parameters that generated it) conditional + on a set of experimental data. This function essentially just computes the kernel density estimate (KDE) + of the simulation data at the experimental data points. + If no experimental data is provided just return the KDE evaluated at default points provided by the fastkde + library. + + Reference: + + Steven Miletić, Brandon M. Turner, Birte U. Forstmann, Leendert van Maanen, + Parameter recovery for the Leaky Competing Accumulator model, + Journal of Mathematical Psychology, + Volume 76, Part A, + 2017, + Pages 25-50, + ISSN 0022-2496, + https://doi.org/10.1016/j.jmp.2016.12.001. + (http://www.sciencedirect.com/science/article/pii/S0022249616301663) + + O’Brien, T. A., Kashinath, K., Cavanaugh, N. R., Collins, W. D. & O’Brien, J. P. + A fast and objective multidimensional kernel density estimation method: fastKDE. + Comput. Stat. Data Anal. 101, 148–160 (2016). + __ + + O’Brien, T. A., Collins, W. D., Rauscher, S. A. & Ringler, T. D. + Reducing the computational cost of the ECF using a nuFFT: A fast and objective probability density estimation method. + Comput. Stat. Data Anal. 79, 222–234 (2014). __ + + Parameters + ---------- + sim_data: This must be a 3D numpy array where the first dimension is the trial, the + second dimension is the simulation number, and the final dimension is data points. + + exp_data: This must be a numpy array with identical format as the simulation data, with the exception + that there is no simulation dimension. + + categorical_dims: a list of indices that indicate categorical dimensions of a data point. + + combine_trials: Combine data across all trials into a single likelihood estimate, this assumes + that the parameters of the simulations are identical across trials. + + Returns + ------- + The pdf of simulation data (or in other words, the generating parameters) conditioned on the + experimental data. + + """ + + if combine_trials: + sim_data = np.vstack(sim_data)[None, :, :] + + con_sim_data = sim_data[:, :, ~categorical_dims] + cat_sim_data = sim_data[:, :, categorical_dims] + + categories = np.unique(cat_sim_data) + kdes = [] + for trial in range(len(con_sim_data)): + s = con_sim_data[trial] + + # Compute a separate KDE for each combination of categorical variables. + dens_u = {} + for category in categories: + + # Get the subset of simulations that correspond to this category + dsub = s[cat_sim_data[trial] == category] + + # If we didn't get enough simulation results for this category, don't do + # a KDE + if len(dsub) < 100: + dens_u[category] = (None, None) + continue + + # Do KDE + fKDE = fastKDE.fastKDE(dsub, doSaveMarginals=False) + pdf = fKDE.pdf + axes = fKDE.axes + + # Scale the pdf by the fraction of simulations that fall within this category + pdf = pdf * (len(dsub)/len(s)) + + # Save the KDE values and axes for this category + dens_u[category] = (pdf, axes) + + kdes.append(dens_u) + + # If we are passed experimental data, evaluate the KDE at the experimental data points + if exp_data is not None: + + kdes_eval = np.zeros((len(exp_data),)) + for trial in range(len(exp_data)): + + # Extract the categorical values for this experimental trial + exp_trial_cat = exp_data[trial, categorical_dims] + + if len(exp_trial_cat) == 1: + exp_trial_cat = exp_trial_cat[0] + + # Get the right KDE for this trial, if all simulation trials have been combined + # use that KDE for all trials of experimental data. + if len(kdes) == 1: + kde, axes = kdes[0][exp_trial_cat] + else: + kde, axes = kdes[trial][exp_trial_cat] + + # Linear interpolation using the grid we computed the KDE + # on. + if kde is not None: + kdes_eval[trial] = interpn(axes, kde, exp_data[trial, ~categorical_dims], + method='linear', bounds_error=False, fill_value=1e-10) + else: + kdes_eval[trial] = 1e-10 + + return kdes_eval + + else: + return kdes + + +def make_likelihood_function(composition: 'psyneulink.core.composition.Composition', + fit_params: typing.List[Parameter], + inputs: typing.Union[np.ndarray, typing.List], + categorical_dims: np.ndarray, + data_to_fit: np.ndarray, + num_simulations: int = 1000, + fixed_params: typing.Optional[typing.Dict[Parameter, typing.Any]] = None, + combine_trials=True): + """ + Construct and return a Python function that returns the log likelihood of experimental + data being generated by a PsyNeuLink composition. The likelihood function is parameterized + by fit_params + + Parameters + ---------- + composition: A PsyNeuLink composition. This function returns a function that runs + many simulations of this composition to generate a kernel density estimate of the likelihood + of a dataset under different parameter settings. The output (composition.results) should match + the format in data_to_fit. + fit_params: A list of PsyNeuLink parameters to fit. Each on of these parameters will map to + an argument of the likelihood function that is returned. Values passed via these arguments + will be assigned to the composition before simulation. + fixed_params: A dict of PsyNeuLink parameters and their corresponding fixed values. These + parameters will be applied to the composition before simulation but will not be exposed as + arguments to the likelihood function. + inputs: A set of inputs to pass to the composition on each simulation of the likelihood. These + inputs are passed directly to the composition run method as is. + categorical_dims: A 1D logical array, where each dimension corresponds to an output dimension + of the PsyNeuLink composition. If True, the dimension should be considered categorical, if False, + it should be treated as continuous. Categorical is suitable for outputs that will only take on + a handful of unique values, such as the decision value of a DDM. + data_to_fit: A 2D numpy array where the first dimension is the trial number and the columns are + in the same format as outputs of the PsyNeuLink composition. This data essentially describes at + what values the KDE of the likelihood should be evaluated. + num_simulations: The number of simulations (per trial) to run to construct the KDE likelihood. + combine_trials: Whether we can combine simulations across trials for one estimate of the likelihood. + This can dramatically increase the speed of the likelihood function by allowing a smaller number + of total simulations to run per trial. However, this cannot be done if the trial by trial state + of the composition is maintained. + + Returns + ------- + A tuple containing: + - the likelihood function + - A dict which maps elements of fit_params to their string function argument names. + """ + + # Get the number of trials in the input data + num_trials = len(next(iter(inputs))) + + # Check to see if any parameters (fittable or fixed) have the same name, + # this will cause problems, if so, we need to create a unique numbering. + # If we have multiple parameters with this name already, assign it the + # next available number + all_param_names = [p.name for p in fit_params] + dupe_counts = [all_param_names[:i].count(all_param_names[i])+1 for i in range(len(all_param_names))] + all_param_names = [name if count == 1 else f"{name}_{count}" for name, count in zip(all_param_names, dupe_counts)] + param_name_map = dict(zip(fit_params, all_param_names)) + + def log_likelihood(**kwargs): + context = Context() + + # Assign parameter values to composition, eventually this should be done + # via the OptimizationControlMechanism and its control allocations stuff. + # However, currently the Python code for that creates a fresh context + # per simulation with considerable overhead. Instead, we create one context + # and use reset_stateful_functions_when=AtTrialStart(). Additionally, all simulations + # are computed in one call to compiled run via setting num_trials to + # num_simulations * len(input). + for param in fit_params: + try: + value = kwargs[param_name_map[param]] + except KeyError: + raise ValueError(f"No argument {param_name_map[param]} passed to likelihood function for Parameter: \n{param}") + + # Apply the parameter value under a fresh context + param.set(value, context) + + # Also apply the fixed parameters + if fixed_params: + for param, value in fixed_params: + param.set(value, context) + + # Run the composition for all simulations, this corresponds to looping over the input + # num_simulations times. + composition.run(inputs=inputs, + reset_stateful_functions_when=AtTrialStart(), + num_trials=num_simulations * num_trials, + bin_execute=True, + context=context) + + results = np.squeeze(np.array(composition.results)) + + # Split results into (trials, simulations, data) + sim_data = np.array(np.vsplit(results, len(input))) + + # Compute the likelihood given the data + like = simulation_likelihood(sim_data=sim_data, exp_data=data_to_fit, + categorical_dims=categorical_dims, + combine_trials=combine_trials) + + # Make 0 densities very small so log doesn't explode + like[like == 0.0] = 1.0e-10 + + return np.sum(np.log(like)) + + return log_likelihood, param_name_map + + +class MaxLikelihoodEstimator: + """ + Implements a maximum likelihood estimation given a likelihood function + """ + + def __init__(self, + log_likelihood_function: typing.Callable, + fit_params_bounds: typing.Dict[str, typing.Tuple], + fixed_params: typing.Optional[typing.Dict[Parameter, typing.Any]]): + self.log_likelihood_function = log_likelihood_function + self.fit_params_bounds = fit_params_bounds + + if fixed_params is not None: + self.fixed_params = fixed_params + else: + self.fixed_params = {} + + def fit(self): + + bounds = list(self.fit_params_bounds.values()) + + # Check if any of are fixed params are in parameters to fit, this is a mistake + for fixed_p, val in self.fixed_params.items(): + if fixed_p in self.fit_params_bounds: + raise ValueError(f"Fixed parameter ({fixed_p}) is also present in the parameters to fit.") + + def print_param_vec(p, end="\n"): + print(', '.join(f'{name}={value:.5f}' for name, value in p.items()), end=end) + + # Create a wrapper function for the objective. + def neg_log_like(x): + params = dict(zip(self.fit_params_bounds.keys(), x)) + # print_param_vec(params, end="") + + p = -self.log_likelihood_function(**self.fixed_params, **params) + # print(f" neg_log_like={p:.5f}") + return p + + t0 = time.time() + + def print_callback(x, convergence): + global t0 + t1 = time.time() + params = dict(zip(self.fit_params_bounds.keys(), x)) + print_param_vec(params, end="") + print(f", convergence={convergence:.5f}, iter_time={t1 - t0} secs") + t0 = t1 + + t0 = time.time() + + # with Progress() as progress: + # opt_task = progress.add_task("Maximum likelihood optimization ...", total=100, start=False) + # + # def progress_callback(x, convergence): + # convergence = 100.0 * convergence + # progress.update(opt_task, completed=convergence) + + r = differential_evolution(neg_log_like, bounds, callback=print_callback, maxiter=500, workers=6) + + print(f"Search Time: {(time.time() - t0) / 60.0} minutes") + + # Bind the fitted parameters to their names + fitted_params = dict(zip(list(self.fit_params_bounds.keys()), r.x)) + print(f"Fitted Params: \n\t{fitted_params}\nLikelihood: {r.fun}") + + # Save all the results + output_dict = { + 'fixed_params': self.fixed_params, + 'fitted_params': fitted_params, + 'likelihood': r.fun, + } + + return output_dict + From 5a8e32510710cc325853b9341e14691a63b3f26a Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 9 Dec 2020 10:36:08 -0500 Subject: [PATCH 007/285] Fixed DDM bug where noise is inside sqrt. --- psyneulink/core/components/functions/fitfunctions.py | 2 +- .../functions/statefulfunctions/integratorfunctions.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py index a6f7eb2ba23..dd5f318f7f6 100644 --- a/psyneulink/core/components/functions/fitfunctions.py +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -66,7 +66,7 @@ def simulation_likelihood(sim_data, """ - if combine_trials: + if combine_trials and sim_data.shape[0] > 1: sim_data = np.vstack(sim_data)[None, :, :] con_sim_data = sim_data[:, :, ~categorical_dims] diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index aff06fe8a65..1afc586e2d5 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2455,7 +2455,7 @@ def _function(self, random_draw = np.array([random_state.normal() for _ in list(variable)]) value = previous_value + rate * variable * time_step_size \ - + np.sqrt(time_step_size * noise) * random_draw + + noise * np.sqrt(time_step_size) * random_draw adjusted_value = np.clip(value + offset, -threshold, threshold) @@ -2513,10 +2513,12 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): val = builder.fmul(val, time_step_size) val = builder.fadd(val, prev_val) - factor = builder.fmul(noise, time_step_size) + #factor = builder.fmul(noise, time_step_size) + factor = time_step_size sqrt_f = ctx.get_builtin("sqrt", [ctx.float_ty]) factor = builder.call(sqrt_f, [factor]) + factor = builder.fmul(noise, factor) factor = builder.fmul(rand_val, factor) val = builder.fadd(val, factor) From 37462493101d00c4f1787314b252ff897303a5f3 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 9 Dec 2020 10:54:43 -0500 Subject: [PATCH 008/285] Moved noise term outside of sqrt term. The standard deviation of the Gaussian noise for the DDM is conventionally defined as noise * sqrt(dt). See Bogacz et al., Eq 6. --- .../functions/statefulfunctions/integratorfunctions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index aff06fe8a65..39459dcea6c 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2455,7 +2455,7 @@ def _function(self, random_draw = np.array([random_state.normal() for _ in list(variable)]) value = previous_value + rate * variable * time_step_size \ - + np.sqrt(time_step_size * noise) * random_draw + + noise * np.sqrt(time_step_size) * random_draw adjusted_value = np.clip(value + offset, -threshold, threshold) @@ -2513,11 +2513,11 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): val = builder.fmul(val, time_step_size) val = builder.fadd(val, prev_val) - factor = builder.fmul(noise, time_step_size) sqrt_f = ctx.get_builtin("sqrt", [ctx.float_ty]) - factor = builder.call(sqrt_f, [factor]) + factor = builder.call(sqrt_f, [time_step_size]) factor = builder.fmul(rand_val, factor) + factor = builder.fmul(factor, noise) val = builder.fadd(val, factor) From 37393b68e6ff9baa0bd1733158138d555a5943ac Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 9 Dec 2020 17:34:47 -0500 Subject: [PATCH 009/285] diffusion integrators: add kwargs to constructors --- .../statefulfunctions/integratorfunctions.py | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index 39459dcea6c..028686d3337 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2378,19 +2378,22 @@ class Parameters(IntegratorFunction.Parameters): ) @tc.typecheck - def __init__(self, - default_variable=None, - rate: tc.optional(parameter_spec) = None, - noise=None, - offset: tc.optional(parameter_spec) = None, - starting_point=None, - threshold=None, - time_step_size=None, - initializer=None, - seed=None, - params: tc.optional(tc.optional(dict)) = None, - owner=None, - prefs: tc.optional(is_pref_set) = None): + def __init__( + self, + default_variable=None, + rate: tc.optional(parameter_spec) = None, + noise=None, + offset: tc.optional(parameter_spec) = None, + starting_point=None, + threshold=None, + time_step_size=None, + initializer=None, + seed=None, + params: tc.optional(tc.optional(dict)) = None, + owner=None, + prefs: tc.optional(is_pref_set) = None, + **kwargs + ): if seed is None: seed = get_global_seed() @@ -2411,6 +2414,7 @@ def __init__(self, params=params, owner=owner, prefs=prefs, + **kwargs ) def _validate_noise(self, noise): @@ -2794,19 +2798,22 @@ class Parameters(IntegratorFunction.Parameters): ) @tc.typecheck - def __init__(self, - default_variable=None, - rate: tc.optional(parameter_spec) = None, - decay=None, - noise=None, - offset: tc.optional(parameter_spec) = None, - starting_point=None, - time_step_size=None, - initializer=None, - params: tc.optional(tc.optional(dict)) = None, - seed=None, - owner=None, - prefs: tc.optional(is_pref_set) = None): + def __init__( + self, + default_variable=None, + rate: tc.optional(parameter_spec) = None, + decay=None, + noise=None, + offset: tc.optional(parameter_spec) = None, + starting_point=None, + time_step_size=None, + initializer=None, + params: tc.optional(tc.optional(dict)) = None, + seed=None, + owner=None, + prefs: tc.optional(is_pref_set) = None, + **kwargs + ): if seed is None: seed = get_global_seed() @@ -2828,6 +2835,7 @@ def __init__(self, random_state=random_state, owner=owner, prefs=prefs, + **kwargs ) def _validate_noise(self, noise): From a817776af65c4417a31b5477864e70ab4d02c62f Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 9 Dec 2020 17:25:26 -0500 Subject: [PATCH 010/285] diffusion integrators: rename starting_point to non_decision_time --- Scripts/Debug/Umemoto_Feb.py | 2 +- Scripts/Debug/Umemoto_Feb2.py | 2 +- Scripts/Debug/markus_test_umemoto.py | 2 +- Scripts/Debug/new_umemoto.py | 2 +- .../statefulfunctions/integratorfunctions.py | 38 ++++++++++--------- .../mechanisms/processing/integrator/ddm.py | 2 +- tests/composition/test_composition.py | 2 +- tests/mechanisms/test_ddm_mechanism.py | 6 +-- tests/mechanisms/test_integrator_mechanism.py | 2 +- 9 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Scripts/Debug/Umemoto_Feb.py b/Scripts/Debug/Umemoto_Feb.py index 558ac57df76..024bc7f667c 100644 --- a/Scripts/Debug/Umemoto_Feb.py +++ b/Scripts/Debug/Umemoto_Feb.py @@ -71,7 +71,7 @@ pnl.VARIABLE: (pnl.OWNER_VALUE, 2), pnl.FUNCTION: pnl.Linear(0, slope=1.0, intercept=1) } - ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),starting_point=(0), t0=0.15 + ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), t0=0.15 Decision.set_log_conditions('InputPort-0')#, log_condition=pnl.PROCESSING) diff --git a/Scripts/Debug/Umemoto_Feb2.py b/Scripts/Debug/Umemoto_Feb2.py index 1a10602c91a..4694058ef46 100644 --- a/Scripts/Debug/Umemoto_Feb2.py +++ b/Scripts/Debug/Umemoto_Feb2.py @@ -73,7 +73,7 @@ pnl.VARIABLE: (pnl.OWNER_VALUE, 2), pnl.FUNCTION: pnl.Linear(0, slope=1.0, intercept=1) } - ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),starting_point=(0), t0=0.15 + ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), t0=0.15 Decision.set_log_conditions('InputPort-0')#, log_condition=pnl.PROCESSING) Decision.set_log_conditions('PROBABILITY_UPPER_THRESHOLD') diff --git a/Scripts/Debug/markus_test_umemoto.py b/Scripts/Debug/markus_test_umemoto.py index eb572dac470..c99c1f0b2df 100644 --- a/Scripts/Debug/markus_test_umemoto.py +++ b/Scripts/Debug/markus_test_umemoto.py @@ -88,7 +88,7 @@ pnl.VARIABLE: (pnl.OWNER_VALUE, 2), pnl.FUNCTION: psyneulink.core.components.functions.transferfunctions.Linear(0, slope=1.0, intercept=1) } - ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),starting_point=(0), t0=0.15 + ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), t0=0.15 print(Decision.execute([1])) diff --git a/Scripts/Debug/new_umemoto.py b/Scripts/Debug/new_umemoto.py index 4a80d8749e9..42e1b1e3119 100644 --- a/Scripts/Debug/new_umemoto.py +++ b/Scripts/Debug/new_umemoto.py @@ -67,7 +67,7 @@ pnl.VARIABLE: (pnl.OWNER_VALUE, 2), pnl.FUNCTION: pnl.Linear(0, slope=1.0, intercept=1) } - ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),starting_point=(0), t0=0.15 + ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), t0=0.15 Decision.set_log_conditions('InputPort-0') diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index 028686d3337..771b21473d2 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2125,7 +2125,7 @@ class DriftDiffusionIntegrator(IntegratorFunction): # ------------------------- rate=1.0, \ noise=0.0, \ offset= 0.0, \ - starting_point=0.0, \ + non_decision_time=0.0, \ threshold=1.0 \ time_step_size=1.0, \ initializer=None, \ @@ -2256,6 +2256,10 @@ class DriftDiffusionIntegrator(IntegratorFunction): # ------------------------- starting_point is a list or array, each of its elements is used as the starting point for each element of the integral. + non_decision_time : float : default 0.0 + specifies the starting time of the model and is used to compute `previous_time + ` + threshold : float determines the boundaries of the drift diffusion process: the integration process can be scheduled to terminate when the result of `function ` equals or exceeds either the @@ -2342,8 +2346,8 @@ class Parameters(IntegratorFunction.Parameters): :type: :read only: True - starting_point - see `starting_point ` + non_decision_time + see `non_decision_time ` :default value: 0.0 :type: ``float`` @@ -2363,10 +2367,10 @@ class Parameters(IntegratorFunction.Parameters): # FIX 6/21/19 [JDC]: MAKE ALL OF THESE PARAMETERS AND ADD function_arg TO THEM TO "PARALLELIZE" INTEGRATION rate = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) - starting_point = 0.0 + non_decision_time = 0.0 threshold = Parameter(100.0, modulable=True) time_step_size = Parameter(1.0, modulable=True) - previous_time = Parameter(None, initializer='starting_point', pnl_internal=True) + previous_time = Parameter(None, initializer='non_decision_time', pnl_internal=True) seed = Parameter(None, read_only=True) random_state = Parameter(None, stateful=True, loggable=False) enable_output_type_conversion = Parameter( @@ -2384,7 +2388,7 @@ def __init__( rate: tc.optional(parameter_spec) = None, noise=None, offset: tc.optional(parameter_spec) = None, - starting_point=None, + non_decision_time=None, threshold=None, time_step_size=None, initializer=None, @@ -2405,7 +2409,7 @@ def __init__( default_variable=default_variable, rate=rate, time_step_size=time_step_size, - starting_point=starting_point, + non_decision_time=non_decision_time, initializer=initializer, threshold=threshold, noise=noise, @@ -2560,7 +2564,7 @@ class OrnsteinUhlenbeckIntegrator(IntegratorFunction): # ---------------------- decay=1.0, \ noise=0.0, \ offset= 0.0, \ - starting_point=0.0, \ + non_decision_time=0.0, \ time_step_size=1.0, \ initializer=0.0, \ params=None, \ @@ -2619,7 +2623,7 @@ class OrnsteinUhlenbeckIntegrator(IntegratorFunction): # ---------------------- if it is a list or array, it must be the same length as `variable ` (see `offset ` for details) - starting_point : float : default 0.0 + non_decision_time : float : default 0.0 specifies the starting time of the model and is used to compute `previous_time ` @@ -2691,7 +2695,7 @@ class OrnsteinUhlenbeckIntegrator(IntegratorFunction): # ---------------------- the corresponding elements of the integral (i.e., Hadamard addition). Serves as *ADDITIVE_PARAM* for `modulation ` of `function `. - starting_point : float + non_decision_time : float determines the start time of the integration process. time_step_size : float @@ -2769,8 +2773,8 @@ class Parameters(IntegratorFunction.Parameters): :default value: 1.0 :type: ``float`` - starting_point - see `starting_point ` + non_decision_time + see `non_decision_time ` :default value: 0.0 :type: ``float`` @@ -2786,8 +2790,8 @@ class Parameters(IntegratorFunction.Parameters): decay = Parameter(1.0, modulable=True) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) time_step_size = Parameter(1.0, modulable=True) - starting_point = 0.0 - previous_time = Parameter(0.0, initializer='starting_point', pnl_internal=True) + non_decision_time = 0.0 + previous_time = Parameter(0.0, initializer='non_decision_time', pnl_internal=True) random_state = Parameter(None, stateful=True, loggable=False) enable_output_type_conversion = Parameter( False, @@ -2805,7 +2809,7 @@ def __init__( decay=None, noise=None, offset: tc.optional(parameter_spec) = None, - starting_point=None, + non_decision_time=None, time_step_size=None, initializer=None, params: tc.optional(tc.optional(dict)) = None, @@ -2826,11 +2830,11 @@ def __init__( decay=decay, noise=noise, offset=offset, - starting_point=starting_point, + non_decision_time=non_decision_time, time_step_size=time_step_size, initializer=initializer, previous_value=initializer, - previous_time=starting_point, + previous_time=non_decision_time, params=params, random_state=random_state, owner=owner, diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index 22c74a72532..73ca1b3112e 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -229,7 +229,7 @@ ... function=pnl.DriftDiffusionIntegrator( ... noise=0.5, ... initializer=1.0, - ... starting_point=2.0, + ... non_decision_time=2.0, ... rate=3.0 ... ), ... name='my_DDM_path_integrator' diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 25a751ad2ed..fa96a60a75f 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -4331,7 +4331,7 @@ class TestSchedulerConditions: ]) def test_scheduler_conditions(self, mode, condition, expected_result): decisionMaker = pnl.DDM( - function=pnl.DriftDiffusionIntegrator(starting_point=0, + function=pnl.DriftDiffusionIntegrator(non_decision_time=0, threshold=1, noise=0.0), reset_stateful_function_when=pnl.AtTrialStart(), diff --git a/tests/mechanisms/test_ddm_mechanism.py b/tests/mechanisms/test_ddm_mechanism.py index f8fda48d54d..c7fbe07d51a 100644 --- a/tests/mechanisms/test_ddm_mechanism.py +++ b/tests/mechanisms/test_ddm_mechanism.py @@ -75,7 +75,7 @@ def test_valid(self): # reset only decision variable D.function.initializer = 1.0 - D.function.starting_point = 0.0 + D.function.non_decision_time = 0.0 D.reset() assert np.allclose(D.function.value[0], 1.0) assert np.allclose(D.function.previous_value, 1.0) @@ -620,7 +620,7 @@ def test_DDM_time(): noise=0.0, rate=-5.0, time_step_size=0.2, - starting_point=0.5 + non_decision_time=0.5 ) ) @@ -660,7 +660,7 @@ def test_DDM_in_composition(benchmark, mode): rate=1, noise=0.0, offset=0.0, - starting_point=0.0, + non_decision_time=0.0, time_step_size=0.1, ), ) diff --git a/tests/mechanisms/test_integrator_mechanism.py b/tests/mechanisms/test_integrator_mechanism.py index a212e4821d3..60acbda1351 100644 --- a/tests/mechanisms/test_integrator_mechanism.py +++ b/tests/mechanisms/test_integrator_mechanism.py @@ -610,7 +610,7 @@ def test_ornstein_uhlenbeck_integrator_time(self): initializer=10.0, rate=10, time_step_size=0.2, - starting_point=0.5, + non_decision_time=0.5, decay=0.1, offset=10, ) From f80fc896ecf813dc5f10567cf5c3bede482483ec Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 9 Dec 2020 17:26:15 -0500 Subject: [PATCH 011/285] diffusion integrators: make non_decision_time modulable --- .../functions/statefulfunctions/integratorfunctions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index 771b21473d2..8b16a3cd7c7 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2367,7 +2367,7 @@ class Parameters(IntegratorFunction.Parameters): # FIX 6/21/19 [JDC]: MAKE ALL OF THESE PARAMETERS AND ADD function_arg TO THEM TO "PARALLELIZE" INTEGRATION rate = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) - non_decision_time = 0.0 + non_decision_time = Parameter(0.0, modulable=True) threshold = Parameter(100.0, modulable=True) time_step_size = Parameter(1.0, modulable=True) previous_time = Parameter(None, initializer='non_decision_time', pnl_internal=True) @@ -2790,7 +2790,7 @@ class Parameters(IntegratorFunction.Parameters): decay = Parameter(1.0, modulable=True) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) time_step_size = Parameter(1.0, modulable=True) - non_decision_time = 0.0 + non_decision_time = Parameter(0.0, modulable=True) previous_time = Parameter(0.0, initializer='non_decision_time', pnl_internal=True) random_state = Parameter(None, stateful=True, loggable=False) enable_output_type_conversion = Parameter( From 0c678614a69a18fe907f8d80532dc19e66769502 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 9 Dec 2020 17:59:32 -0500 Subject: [PATCH 012/285] diffusion integrators: make starting_point an alias of initializer --- .../statefulfunctions/integratorfunctions.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index 8b16a3cd7c7..7c97cf02456 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2367,6 +2367,7 @@ class Parameters(IntegratorFunction.Parameters): # FIX 6/21/19 [JDC]: MAKE ALL OF THESE PARAMETERS AND ADD function_arg TO THEM TO "PARALLELIZE" INTEGRATION rate = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) + initializer = Parameter(np.array([0]), aliases=['starting_point']) non_decision_time = Parameter(0.0, modulable=True) threshold = Parameter(100.0, modulable=True) time_step_size = Parameter(1.0, modulable=True) @@ -2388,10 +2389,10 @@ def __init__( rate: tc.optional(parameter_spec) = None, noise=None, offset: tc.optional(parameter_spec) = None, + starting_point=None, non_decision_time=None, threshold=None, time_step_size=None, - initializer=None, seed=None, params: tc.optional(tc.optional(dict)) = None, owner=None, @@ -2409,8 +2410,8 @@ def __init__( default_variable=default_variable, rate=rate, time_step_size=time_step_size, + starting_point=starting_point, non_decision_time=non_decision_time, - initializer=initializer, threshold=threshold, noise=noise, offset=offset, @@ -2790,6 +2791,7 @@ class Parameters(IntegratorFunction.Parameters): decay = Parameter(1.0, modulable=True) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) time_step_size = Parameter(1.0, modulable=True) + initializer = Parameter(np.array([0]), aliases=['starting_point']) non_decision_time = Parameter(0.0, modulable=True) previous_time = Parameter(0.0, initializer='non_decision_time', pnl_internal=True) random_state = Parameter(None, stateful=True, loggable=False) @@ -2811,7 +2813,7 @@ def __init__( offset: tc.optional(parameter_spec) = None, non_decision_time=None, time_step_size=None, - initializer=None, + starting_point=None, params: tc.optional(tc.optional(dict)) = None, seed=None, owner=None, @@ -2832,8 +2834,8 @@ def __init__( offset=offset, non_decision_time=non_decision_time, time_step_size=time_step_size, - initializer=initializer, - previous_value=initializer, + starting_point=starting_point, + previous_value=starting_point, previous_time=non_decision_time, params=params, random_state=random_state, From 92b92d266d9611c4f5da81fe53f10e91832b577a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 9 Dec 2020 18:09:38 -0500 Subject: [PATCH 013/285] diffusion integrators, analytical: rename starting_point to starting_value --- ...on_Reward_rate_with_penalty_with_inputs.py | 2 +- Scripts/Debug/StabilityFlexibility.py | 4 +- Scripts/Debug/Umemoto_Feb.py | 2 +- Scripts/Debug/Umemoto_Feb2.py | 2 +- Scripts/Debug/markus_test_umemoto.py | 2 +- Scripts/Debug/new_umemoto.py | 2 +- Scripts/Debug/stability_flexibility_simple.py | 4 +- Scripts/Examples/EVC OCM.py | 2 +- Scripts/Examples/EVC-Gratton Composition.py | 2 +- .../Examples/EVC-Gratton-GaussianProcess.py | 2 +- Scripts/Examples/Mixed-NN-and-DDM.py | 2 +- Scripts/Examples/RL-DDM.py | 2 +- Scripts/Examples/StabilityFlexibility.py | 4 +- Scripts/Examples/Stroop Model.py | 2 +- .../Examples/Tutorial/Stroop Model - EVC.py | 2 +- .../ColorMotionTask_FULL.py | 2 +- .../ColorMotionTask_SIMPLE.py | 2 +- .../functions/distributionfunctions.py | 58 +++++++++---------- .../statefulfunctions/integratorfunctions.py | 26 ++++----- .../mechanisms/processing/integrator/ddm.py | 16 ++--- tests/composition/test_composition.py | 4 +- tests/composition/test_control.py | 18 +++--- tests/composition/test_models.py | 2 +- tests/functions/test_distribution.py | 6 +- tests/json/model_with_control.py | 2 +- tests/log/test_log.py | 2 +- tests/mechanisms/test_ddm_mechanism.py | 2 +- .../test_drift_diffusion_analytical.py | 6 +- tests/scheduling/test_system_newsched.py | 2 +- 29 files changed, 92 insertions(+), 92 deletions(-) diff --git a/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py b/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py index a32828ea2aa..169ac54c3c5 100644 --- a/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py +++ b/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py @@ -120,7 +120,7 @@ def get_stroop_model(unit_noise_std=.01, dec_noise_std=.1): function=pnl.DriftDiffusionAnalytical(drift_rate=1, threshold =1, noise=1, - starting_point=0, + starting_value=0, t0=0.35), output_ports=[pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD, diff --git a/Scripts/Debug/StabilityFlexibility.py b/Scripts/Debug/StabilityFlexibility.py index ff9f01cb5d6..2df05100a6d 100644 --- a/Scripts/Debug/StabilityFlexibility.py +++ b/Scripts/Debug/StabilityFlexibility.py @@ -69,7 +69,7 @@ def computeAccuracy(variable): gain = np.asarray([[g]]) DRIFT = 1 # Drift Rate -STARTING_POINT = 0.0 # Starting Point +STARTING_VALUE = 0.0 # Starting Point THRESHOLD = 0.0475 # Threshold NOISE = 0.04 # Noise T0 = 0.2 # T0 @@ -123,7 +123,7 @@ def computeAccuracy(variable): ddmCombination.set_log_conditions([pnl.RESULT]) decisionMaker = pnl.DDM(function=pnl.DriftDiffusionAnalytical(drift_rate = DRIFT, - starting_point = STARTING_POINT, + starting_value = STARTING_VALUE, threshold = THRESHOLD, noise = NOISE, t0 = T0), diff --git a/Scripts/Debug/Umemoto_Feb.py b/Scripts/Debug/Umemoto_Feb.py index 024bc7f667c..7259fd17e76 100644 --- a/Scripts/Debug/Umemoto_Feb.py +++ b/Scripts/Debug/Umemoto_Feb.py @@ -59,7 +59,7 @@ # drift_rate=(0.1170), threshold=(thresh), noise=(c), - starting_point=(x_0), + starting_value=(x_0), t0=t0 ),name='Decision', output_ports=[ diff --git a/Scripts/Debug/Umemoto_Feb2.py b/Scripts/Debug/Umemoto_Feb2.py index 4694058ef46..9d58c12b08c 100644 --- a/Scripts/Debug/Umemoto_Feb2.py +++ b/Scripts/Debug/Umemoto_Feb2.py @@ -61,7 +61,7 @@ # drift_rate=(0.1170), threshold=(thresh), noise=(c), - starting_point=(x_0), + starting_value=(x_0), t0=t0 ),name='Decision', output_ports=[ diff --git a/Scripts/Debug/markus_test_umemoto.py b/Scripts/Debug/markus_test_umemoto.py index c99c1f0b2df..5905194e0ba 100644 --- a/Scripts/Debug/markus_test_umemoto.py +++ b/Scripts/Debug/markus_test_umemoto.py @@ -76,7 +76,7 @@ # drift_rate=(0.3), threshold=(thresh), noise=(c), - starting_point=(x_0), + starting_value=(x_0), t0=t0 ),name='Decision', output_ports=[ diff --git a/Scripts/Debug/new_umemoto.py b/Scripts/Debug/new_umemoto.py index 42e1b1e3119..685b4a9c869 100644 --- a/Scripts/Debug/new_umemoto.py +++ b/Scripts/Debug/new_umemoto.py @@ -55,7 +55,7 @@ # drift_rate=(0.1170), threshold=(thresh), noise=(c), - starting_point=(x_0), + starting_value=(x_0), t0=t0 ),name='Decision', output_ports=[ diff --git a/Scripts/Debug/stability_flexibility_simple.py b/Scripts/Debug/stability_flexibility_simple.py index 0aba89c3a3c..81752af3999 100644 --- a/Scripts/Debug/stability_flexibility_simple.py +++ b/Scripts/Debug/stability_flexibility_simple.py @@ -64,7 +64,7 @@ def computeAccuracy(variable): gain = np.asarray([[g]]) DRIFT = 1 # Drift Rate -STARTING_POINT = 0.0 # Starting Point +STARTING_VALUE = 0.0 # Starting Point THRESHOLD = 0.0475 # Threshold NOISE = 0.04 # Noise T0 = 0.2 # T0 @@ -116,7 +116,7 @@ def computeAccuracy(variable): ddmCombination.set_log_conditions([pnl.RESULT]) decisionMaker = pnl.DDM(function=pnl.DriftDiffusionAnalytical(drift_rate=DRIFT, - starting_point=STARTING_POINT, + starting_value=STARTING_VALUE, threshold=THRESHOLD, noise=NOISE, t0=T0), diff --git a/Scripts/Examples/EVC OCM.py b/Scripts/Examples/EVC OCM.py index 76eb649560f..4c0882b0da9 100644 --- a/Scripts/Examples/EVC OCM.py +++ b/Scripts/Examples/EVC OCM.py @@ -16,7 +16,7 @@ ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)})), noise=0.5, - starting_point=0, + starting_value=0, t0=0.45), output_ports=[DECISION_VARIABLE, RESPONSE_TIME, diff --git a/Scripts/Examples/EVC-Gratton Composition.py b/Scripts/Examples/EVC-Gratton Composition.py index a97c770468d..fc900390543 100644 --- a/Scripts/Examples/EVC-Gratton Composition.py +++ b/Scripts/Examples/EVC-Gratton Composition.py @@ -18,7 +18,7 @@ function=pnl.DriftDiffusionAnalytical(drift_rate=(1.0), threshold=(0.2645), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.15), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, diff --git a/Scripts/Examples/EVC-Gratton-GaussianProcess.py b/Scripts/Examples/EVC-Gratton-GaussianProcess.py index 1b615ff79a9..11d9f5a0f4c 100644 --- a/Scripts/Examples/EVC-Gratton-GaussianProcess.py +++ b/Scripts/Examples/EVC-Gratton-GaussianProcess.py @@ -34,7 +34,7 @@ ), ), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.45 ), output_ports=[ diff --git a/Scripts/Examples/Mixed-NN-and-DDM.py b/Scripts/Examples/Mixed-NN-and-DDM.py index 8e1273474d8..7d53d6a20e6 100644 --- a/Scripts/Examples/Mixed-NN-and-DDM.py +++ b/Scripts/Examples/Mixed-NN-and-DDM.py @@ -20,7 +20,7 @@ function=psyneulink.core.components.functions.distributionfunctions.DriftDiffusionAnalytical( drift_rate=0.5, threshold=1, - starting_point=0.0 + starting_value=0.0 ) ) diff --git a/Scripts/Examples/RL-DDM.py b/Scripts/Examples/RL-DDM.py index 7a4f97709c4..9573b69e302 100644 --- a/Scripts/Examples/RL-DDM.py +++ b/Scripts/Examples/RL-DDM.py @@ -29,7 +29,7 @@ function=psyneulink.core.components.functions.distributionfunctions.DriftDiffusionAnalytical( drift_rate=pnl.CONTROL, threshold=pnl.CONTROL, - starting_point=pnl.CONTROL, + starting_value=pnl.CONTROL, noise=pnl.CONTROL, ), output_ports=[pnl.SELECTED_INPUT_ARRAY], diff --git a/Scripts/Examples/StabilityFlexibility.py b/Scripts/Examples/StabilityFlexibility.py index 4a4e0db23c3..41686e7a94e 100644 --- a/Scripts/Examples/StabilityFlexibility.py +++ b/Scripts/Examples/StabilityFlexibility.py @@ -70,7 +70,7 @@ def computeAccuracy(variable): gain = np.asarray([[g]]) DRIFT = 1 # Drift Rate -STARTING_POINT = 0.0 # Starting Point +STARTING_VALUE = 0.0 # Starting Point THRESHOLD = 0.0475 # Threshold NOISE = 0.04 # Noise T0 = 0.2 # T0 @@ -124,7 +124,7 @@ def computeAccuracy(variable): ddmCombination.set_log_conditions([pnl.RESULT]) decisionMaker = pnl.DDM(function=pnl.DriftDiffusionAnalytical(drift_rate = DRIFT, - starting_point = STARTING_POINT, + starting_value = STARTING_VALUE, threshold = THRESHOLD, noise = NOISE, t0 = T0), diff --git a/Scripts/Examples/Stroop Model.py b/Scripts/Examples/Stroop Model.py index 16925530d64..0e916ad5c70 100644 --- a/Scripts/Examples/Stroop Model.py +++ b/Scripts/Examples/Stroop Model.py @@ -32,7 +32,7 @@ function=DriftDiffusionAnalytical(drift_rate=(1.0), threshold=(0.2645), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.15), output_ports=[DECISION_VARIABLE, RESPONSE_TIME, diff --git a/Scripts/Examples/Tutorial/Stroop Model - EVC.py b/Scripts/Examples/Tutorial/Stroop Model - EVC.py index 0cb1d3986a9..053e559e82c 100644 --- a/Scripts/Examples/Tutorial/Stroop Model - EVC.py +++ b/Scripts/Examples/Tutorial/Stroop Model - EVC.py @@ -32,7 +32,7 @@ function=DriftDiffusionAnalytical(drift_rate=(1.0), threshold=(0.2645), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.15), output_ports=[DECISION_VARIABLE, RESPONSE_TIME, diff --git a/Scripts/Models (Under Development)/ColorMotionTask_FULL.py b/Scripts/Models (Under Development)/ColorMotionTask_FULL.py index 8c9f79c5e85..b0bbfc7e1e4 100644 --- a/Scripts/Models (Under Development)/ColorMotionTask_FULL.py +++ b/Scripts/Models (Under Development)/ColorMotionTask_FULL.py @@ -122,7 +122,7 @@ def incongruent(colorResponse, motionResponse): function=Linear(slope=optimal_motion_control)) decision = DDM(name='Decision', function=DriftDiffusionAnalytical( - starting_point=0, + starting_value=0, noise=0.5, t0=0.2, threshold=0.45), diff --git a/Scripts/Models (Under Development)/ColorMotionTask_SIMPLE.py b/Scripts/Models (Under Development)/ColorMotionTask_SIMPLE.py index 58f220b2738..9fa8567715c 100644 --- a/Scripts/Models (Under Development)/ColorMotionTask_SIMPLE.py +++ b/Scripts/Models (Under Development)/ColorMotionTask_SIMPLE.py @@ -9,7 +9,7 @@ function=Linear(slope=optimal_motion_control)) decision = DDM(name='Decision', function=DriftDiffusionAnalytical( - starting_point=0, + starting_value=0, noise=0.5, t0=0.2, threshold=0.45), diff --git a/psyneulink/core/components/functions/distributionfunctions.py b/psyneulink/core/components/functions/distributionfunctions.py index 325aa8c8e6a..846f37fb1d8 100644 --- a/psyneulink/core/components/functions/distributionfunctions.py +++ b/psyneulink/core/components/functions/distributionfunctions.py @@ -41,7 +41,7 @@ __all__ = [ 'DistributionFunction', 'DRIFT_RATE', 'DRIFT_RATE_VARIABILITY', 'DriftDiffusionAnalytical', 'ExponentialDist', - 'GammaDist', 'NON_DECISION_TIME', 'NormalDist', 'STARTING_POINT', 'STARTING_POINT_VARIABILITY', + 'GammaDist', 'NON_DECISION_TIME', 'NormalDist', 'STARTING_VALUE', 'STARTING_VALUE_VARIABILITY', 'THRESHOLD_VARIABILITY', 'UniformDist', 'UniformToNormalDist', 'WaldDist', ] @@ -912,16 +912,16 @@ def _function(self, DRIFT_RATE = 'drift_rate' DRIFT_RATE_VARIABILITY = 'DDM_DriftRateVariability' THRESHOLD_VARIABILITY = 'DDM_ThresholdRateVariability' -STARTING_POINT = 'starting_point' -STARTING_POINT_VARIABILITY = "DDM_StartingPointVariability" +STARTING_VALUE = 'starting_value' +STARTING_VALUE_VARIABILITY = "DDM_StartingPointVariability" NON_DECISION_TIME = 't0' def _DriftDiffusionAnalytical_bias_getter(owning_component=None, context=None): - starting_point = owning_component.parameters.starting_point._get(context) + starting_value = owning_component.parameters.starting_value._get(context) threshold = owning_component.parameters.threshold._get(context) try: - return (starting_point + threshold) / (2 * threshold) + return (starting_value + threshold) / (2 * threshold) except TypeError: return None @@ -933,7 +933,7 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- default_variable=None, \ drift_rate=1.0, \ threshold=1.0, \ - starting_point=0.0, \ + starting_value=0.0, \ t0=0.2 \ noise=0.5, \ params=None, \ @@ -949,7 +949,7 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- *Modulatory Parameters:* | *MULTIPLICATIVE_PARAM:* `drift_rate ` - | *ADDITIVE_PARAM:* `starting_point ` + | *ADDITIVE_PARAM:* `starting_value ` | Arguments @@ -967,7 +967,7 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- specifies the threshold (boundary) of the drift diffusion process. If it is a list or array, it must be the same length as `default_variable `. - starting_point : float, list or 1d array : default 1.0 + starting_value : float, list or 1d array : default 1.0 specifies the initial value of the decision variable for the drift diffusion process. If it is a list or array, it must be the same length as `default_variable `. @@ -1013,7 +1013,7 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- determines the threshold (boundary) of the drift diffusion process (i.e., at which the integration process is assumed to terminate). - starting_point : float or 1d array + starting_value : float or 1d array determines the initial value of the decision variable for the drift diffusion process. noise : float or 1d array @@ -1025,7 +1025,7 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- bias : float or 1d array normalized starting point: - (`starting_point ` + `threshold `) / + (`starting_value ` + `threshold `) / (2 * `threshold `) owner : Component @@ -1075,8 +1075,8 @@ class Parameters(DistributionFunction.Parameters): :default value: 0.5 :type: ``float`` - starting_point - see `starting_point ` + starting_value + see `starting_value ` :default value: 0.0 :type: ``float`` @@ -1094,7 +1094,7 @@ class Parameters(DistributionFunction.Parameters): :type: ``float`` """ drift_rate = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) - starting_point = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) + starting_value = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) threshold = Parameter(1.0, modulable=True) noise = Parameter(0.5, modulable=True) t0 = Parameter(.200, modulable=True) @@ -1113,7 +1113,7 @@ class Parameters(DistributionFunction.Parameters): def __init__(self, default_variable=None, drift_rate: tc.optional(parameter_spec) = None, - starting_point: tc.optional(parameter_spec) = None, + starting_value: tc.optional(parameter_spec) = None, threshold: tc.optional(parameter_spec) = None, noise: tc.optional(parameter_spec) = None, t0: tc.optional(parameter_spec) = None, @@ -1127,7 +1127,7 @@ def __init__(self, super().__init__( default_variable=default_variable, drift_rate=drift_rate, - starting_point=starting_point, + starting_value=starting_value, threshold=threshold, noise=noise, t0=t0, @@ -1208,17 +1208,17 @@ def _function(self, stimulus_drift_rate = float(variable) drift_rate = attentional_drift_rate * stimulus_drift_rate threshold = self._get_current_parameter_value(THRESHOLD, context) - starting_point = float(self._get_current_parameter_value(STARTING_POINT, context)) + starting_value = float(self._get_current_parameter_value(STARTING_VALUE, 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) - # starting_point = float(self.starting_point) + # starting_value = float(self.starting_value) # noise = float(self.noise) # t0 = float(self.t0) - bias = (starting_point + threshold) / (2 * threshold) + bias = (starting_value + threshold) / (2 * threshold) # Prevents div by 0 issue below: if bias <= 0: @@ -1310,7 +1310,7 @@ def _function(self, moments['mean_rt_minus'], moments['var_rt_minus'], moments['skew_rt_minus'] @staticmethod - def _compute_conditional_rt_moments(drift_rate, noise, threshold, starting_point, t0): + def _compute_conditional_rt_moments(drift_rate, noise, threshold, starting_value, t0): """ This is a helper function for computing the conditional decison time moments for the DDM. It is based completely off of Matlab\\DDMFunctions\\ddm_metrics_cond_Mat.m. @@ -1318,7 +1318,7 @@ def _compute_conditional_rt_moments(drift_rate, noise, threshold, starting_point :param drift_rate: The drift rate of the DDM :param noise: The diffusion rate. :param threshold: The symmetric threshold of the DDM - :param starting_point: The initial condition. + :param starting_value: The initial condition. :param t0: The non decision time. :return: A dictionary containing the following key value pairs: mean_rt_plus: The mean RT of positive responses. @@ -1330,12 +1330,12 @@ def _compute_conditional_rt_moments(drift_rate, noise, threshold, starting_point """ # transform starting point to be centered at 0 - starting_point = (starting_point - 0.5) * 2.0 * threshold + starting_value = (starting_value - 0.5) * 2.0 * threshold if abs(drift_rate) < 0.01: drift_rate = 0.01 - X = drift_rate * starting_point / noise**2 + X = drift_rate * starting_value / noise**2 Z = drift_rate * threshold / noise**2 X = max(-100, min(100, X)) @@ -1394,7 +1394,7 @@ def load_scalar_param(name): attentional_drift_rate = load_scalar_param(DRIFT_RATE) threshold = load_scalar_param(THRESHOLD) - starting_point = load_scalar_param(STARTING_POINT) + starting_value = load_scalar_param(STARTING_VALUE) noise = load_scalar_param(NOISE) t0 = load_scalar_param(NON_DECISION_TIME) @@ -1407,7 +1407,7 @@ def load_scalar_param(name): drift_rate = builder.fmul(attentional_drift_rate, stimulus_drift_rate) threshold_2 = builder.fmul(threshold, threshold.type(2)) - bias = builder.fadd(starting_point, threshold) + bias = builder.fadd(starting_value, threshold) bias = builder.fdiv(bias, threshold_2) bias = pnlvm.helpers.fclamp(builder, bias, 1e-8, 1 - 1e-8) @@ -1529,16 +1529,16 @@ def _get_arg_out_ptr(idx): skew_rt_minus_ptr = _get_arg_out_ptr(7) # Transform starting point to be centered at 0 - starting_point = bias - starting_point = builder.fsub(starting_point, starting_point.type(0.5)) - starting_point = builder.fmul(starting_point, starting_point.type(2)) - starting_point = builder.fmul(starting_point, threshold) + starting_value = bias + starting_value = builder.fsub(starting_value, starting_value.type(0.5)) + starting_value = builder.fmul(starting_value, starting_value.type(2)) + starting_value = builder.fmul(starting_value, threshold) drift_rate_limit = abs_drift_rate.type(0.01) small_drift = builder.fcmp_ordered("<", abs_drift_rate, drift_rate_limit) drift_rate = builder.select(small_drift, drift_rate_limit, drift_rate) - X = builder.fmul(drift_rate, starting_point) + X = builder.fmul(drift_rate, starting_value) X = builder.fdiv(X, noise_sqr) X = pnlvm.helpers.fclamp(builder, X, X.type(-100), X.type(100)) diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index 7c97cf02456..819ba740fae 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2182,10 +2182,10 @@ class DriftDiffusionIntegrator(IntegratorFunction): # ------------------------- if it is a list or array, it must be the same length as `variable ` (see `offset ` for details). - starting_point : float, list or 1d array: default 0.0 + starting_value : float, list or 1d array: default 0.0 specifies the starting value for the integration process; if it is a list or array, it must be the - same length as `variable ` (see `starting_point - ` for details). + same length as `variable ` (see `starting_value + ` for details). threshold : float : default 0.0 specifies the threshold (boundaries) of the drift diffusion process -- i.e., at which the @@ -2249,11 +2249,11 @@ class DriftDiffusionIntegrator(IntegratorFunction): # ------------------------- the corresponding elements of the integral (i.e., Hadamard addition). Serves as *ADDITIVE_PARAM* for `modulation ` of `function `. - starting_point : float or 1d array + starting_value : float or 1d array determines the starting value for the integration process; if it is a list or array, it must be the same length as `variable `. If `variable ` - is an array and starting_point is a float, starting_point is used for each element of the integral; if - starting_point is a list or array, each of its elements is used as the starting point for each element of the + is an array and starting_value is a float, starting_value is used for each element of the integral; if + starting_value is a list or array, each of its elements is used as the starting point for each element of the integral. non_decision_time : float : default 0.0 @@ -2367,7 +2367,7 @@ class Parameters(IntegratorFunction.Parameters): # FIX 6/21/19 [JDC]: MAKE ALL OF THESE PARAMETERS AND ADD function_arg TO THEM TO "PARALLELIZE" INTEGRATION rate = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) - initializer = Parameter(np.array([0]), aliases=['starting_point']) + initializer = Parameter(np.array([0]), aliases=['starting_value']) non_decision_time = Parameter(0.0, modulable=True) threshold = Parameter(100.0, modulable=True) time_step_size = Parameter(1.0, modulable=True) @@ -2389,7 +2389,7 @@ def __init__( rate: tc.optional(parameter_spec) = None, noise=None, offset: tc.optional(parameter_spec) = None, - starting_point=None, + starting_value=None, non_decision_time=None, threshold=None, time_step_size=None, @@ -2410,7 +2410,7 @@ def __init__( default_variable=default_variable, rate=rate, time_step_size=time_step_size, - starting_point=starting_point, + starting_value=starting_value, non_decision_time=non_decision_time, threshold=threshold, noise=noise, @@ -2791,7 +2791,7 @@ class Parameters(IntegratorFunction.Parameters): decay = Parameter(1.0, modulable=True) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) time_step_size = Parameter(1.0, modulable=True) - initializer = Parameter(np.array([0]), aliases=['starting_point']) + initializer = Parameter(np.array([0]), aliases=['starting_value']) non_decision_time = Parameter(0.0, modulable=True) previous_time = Parameter(0.0, initializer='non_decision_time', pnl_internal=True) random_state = Parameter(None, stateful=True, loggable=False) @@ -2813,7 +2813,7 @@ def __init__( offset: tc.optional(parameter_spec) = None, non_decision_time=None, time_step_size=None, - starting_point=None, + starting_value=None, params: tc.optional(tc.optional(dict)) = None, seed=None, owner=None, @@ -2834,8 +2834,8 @@ def __init__( offset=offset, non_decision_time=non_decision_time, time_step_size=time_step_size, - starting_point=starting_point, - previous_value=starting_point, + starting_value=starting_value, + previous_value=starting_value, previous_time=non_decision_time, params=params, random_state=random_state, diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index 73ca1b3112e..e7f8645d2ba 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -205,7 +205,7 @@ >>> my_DDM_DriftDiffusionAnalytical = pnl.DDM( ... function=pnl.DriftDiffusionAnalytical( ... drift_rate=0.08928, - ... starting_point=0.5, + ... starting_value=0.5, ... threshold=0.2645, ... noise=0.5, ... t0=0.15 @@ -266,7 +266,7 @@ function=DriftDiffusionAnalytical(drift_rate=0.1), params={ DRIFT_RATE:(0.2, ControlProjection), - STARTING_POINT:-0.5 + STARTING_VALUE:-0.5 }, ) The parameters for the DDM when `function ` is set to `DriftDiffusionAnalytical` are: @@ -281,7 +281,7 @@ component, and the input its "stimulus" component. The product of all three determines the drift rate in effect for each time_step of the decision process. .. - * `STARTING_POINT ` (default 0.0) + * `STARTING_VALUE ` (default 0.0) - specifies the starting value of the decision variable. .. * `THRESHOLD` (default 1.0) @@ -367,7 +367,7 @@ from psyneulink.core.components.functions.statefulfunctions.integratorfunctions import \ DriftDiffusionIntegrator, IntegratorFunction -from psyneulink.core.components.functions.distributionfunctions import STARTING_POINT, \ +from psyneulink.core.components.functions.distributionfunctions import STARTING_VALUE, \ DriftDiffusionAnalytical from psyneulink.core.components.functions.combinationfunctions import Reduce from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import _is_control_spec @@ -453,7 +453,7 @@ class DDM(ProcessingMechanism): Arguments --------- - default_variable : value, list or np.ndarray : default FUNCTION_PARAMS[STARTING_POINT] + default_variable : value, list or np.ndarray : default FUNCTION_PARAMS[STARTING_VALUE] the input to the Mechanism used if none is provided in a call to its `execute ` or `run ` methods; also serves as a template to specify the length of the `variable ` for its `function `, and the `primary OutputPort ` of the @@ -471,7 +471,7 @@ class DDM(ProcessingMechanism): Attributes ---------- - variable : value : default FUNCTION_PARAMS[STARTING_POINT] + variable : value : default FUNCTION_PARAMS[STARTING_VALUE] the input to Mechanism's execute method. Serves as the "stimulus" component of the `function `'s **drift_rate** parameter. @@ -707,7 +707,7 @@ class Parameters(ProcessingMechanism.Parameters): function = Parameter( DriftDiffusionAnalytical( drift_rate=1.0, - starting_point=0.0, + starting_value=0.0, threshold=1.0, noise=0.5, t0=.200, @@ -826,7 +826,7 @@ def __init__(self, # compared to other mechanisms: see TransferMechanism.py __init__ function for a more normal example. if default_variable is None and size is None: try: - default_variable = params[FUNCTION_PARAMS][STARTING_POINT] + default_variable = params[FUNCTION_PARAMS][STARTING_VALUE] if not is_numeric(default_variable): # set normally by default default_variable = None diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index fa96a60a75f..48f718ab384 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -7065,7 +7065,7 @@ def test_danglingControlledMech(self): drift_rate=(1.0), threshold=(0.1654), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.25, ), name='Decision', @@ -7095,7 +7095,7 @@ def test_danglingControlledMech(self): ), ), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.45 ), name='second_DDM', diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 5f1f7458094..13bc8e732a1 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -134,7 +134,7 @@ def test_deferred_init(self): 1.01, 0.3)})), noise=0.5, - starting_point=0, + starting_value=0, t0=0.45), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, @@ -771,7 +771,7 @@ def test_multilevel_ocm_gridsearch_minimize(self, mode, benchmark): def test_two_tier_ocm(self): integrationConstant = 0.8 # Time Constant DRIFT = 0.25 # Drift Rate - STARTING_POINT = 0.0 # Starting Point + STARTING_VALUE = 0.0 # Starting Point THRESHOLD = 0.05 # Threshold NOISE = 0.1 # Noise T0 = 0.2 # T0 @@ -826,7 +826,7 @@ def test_two_tier_ocm(self): name="Drift = Wa*(S1 + S2) + (S1*Act1 + S2*Act2)") decisionMaker = pnl.DDM(function=pnl.DriftDiffusionAnalytical(drift_rate=DRIFT, - starting_point=STARTING_POINT, + starting_value=STARTING_VALUE, threshold=THRESHOLD, noise=NOISE, t0=T0), @@ -1163,7 +1163,7 @@ def test_evc(self): pnl.ControlProjection(function=pnl.Linear, control_signal_params={pnl.ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)})), noise=0.5, - starting_point=0, + starting_value=0, t0=0.45), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, @@ -1271,7 +1271,7 @@ def test_evc_gratton(self): function=pnl.DriftDiffusionAnalytical(drift_rate=(1.0), threshold=(0.2645), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.15), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, @@ -1442,7 +1442,7 @@ def test_laming_validation_specify_control_signals(self): drift_rate=1.0, threshold=1.0, noise=0.5, - starting_point=0, + starting_value=0, t0=0.45 ), output_ports=[ @@ -1580,7 +1580,7 @@ def test_stateful_mechanism_in_simulation(self): ), ), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.45 ), output_ports=[ @@ -1895,7 +1895,7 @@ def computeAccuracy(trialInformation): # Constants as defined in Musslick et al. 2018 tau = 0.9 # Time Constant DRIFT = 1 # Drift Rate - STARTING_POINT = 0.0 # Starting Point + STARTING_VALUE = 0.0 # Starting Point THRESHOLD = 0.0475 # Threshold NOISE = 0.04 # Noise T0 = 0.2 # T0 @@ -1945,7 +1945,7 @@ def computeAccuracy(trialInformation): name="Drift = (S1 + S2) + (S1*Activity1 + S2*Activity2)") decisionMaker = pnl.DDM(function=pnl.DriftDiffusionAnalytical(drift_rate=DRIFT, - starting_point=STARTING_POINT, + starting_value=STARTING_VALUE, threshold=THRESHOLD, noise=NOISE, t0=T0), diff --git a/tests/composition/test_models.py b/tests/composition/test_models.py index 2fa220d87e4..3fc3749965e 100644 --- a/tests/composition/test_models.py +++ b/tests/composition/test_models.py @@ -13,7 +13,7 @@ def test_DDM(self): function=psyneulink.core.components.functions.distributionfunctions.DriftDiffusionAnalytical( drift_rate=(1.0), threshold=(10.0), - starting_point=0.0, + starting_value=0.0, ), name='My_DDM', ) diff --git a/tests/functions/test_distribution.py b/tests/functions/test_distribution.py index 31c7da091c1..073e77558e7 100644 --- a/tests/functions/test_distribution.py +++ b/tests/functions/test_distribution.py @@ -16,12 +16,12 @@ test_data = [ (Functions.DriftDiffusionAnalytical, test_var, {}, None, (1.9774974807292212, 0.012242689689501842, 1.9774974807292207, 1.3147677945132479, 1.7929299891370192, 1.9774974807292207, 1.3147677945132479, 1.7929299891370192)), - (Functions.DriftDiffusionAnalytical, test_var, {"drift_rate": RAND1, "threshold": RAND2, "starting_point": RAND3, "t0":RAND4, "noise": RAND5}, None, + (Functions.DriftDiffusionAnalytical, test_var, {"drift_rate": RAND1, "threshold": RAND2, "starting_value": RAND3, "t0":RAND4, "noise": RAND5}, None, (0.4236547993389047, -2.7755575615628914e-17, 0.5173675420165031, 0.06942854144616283, 6.302631815990666, 1.4934079600147951, 0.4288991185241868, 1.7740760781361433)), - (Functions.DriftDiffusionAnalytical, -test_var, {"drift_rate": RAND1, "threshold": RAND2, "starting_point": RAND3, "t0":RAND4, "noise": RAND5}, None, + (Functions.DriftDiffusionAnalytical, -test_var, {"drift_rate": RAND1, "threshold": RAND2, "starting_value": RAND3, "t0":RAND4, "noise": RAND5}, None, (0.42365479933890504, 0.0, 0.5173675420165031, 0.06942854144616283, 6.302631815990666, 1.4934079600147951, 0.4288991185241868, 1.7740760781361433)), # FIXME: Rounding errors result in different behaviour on different platforms -# (Functions.DriftDiffusionAnalytical, 1e-4, {"drift_rate": 1e-5, "threshold": RAND2, "starting_point": RAND3, "t0":RAND4, "noise": RAND5}, "Rounding errors", +# (Functions.DriftDiffusionAnalytical, 1e-4, {"drift_rate": 1e-5, "threshold": RAND2, "starting_value": RAND3, "t0":RAND4, "noise": RAND5}, "Rounding errors", # (0.5828813465336954, 0.04801236718458773, 0.532471083815943, 0.09633801362499317, 6.111833139205608, 1.5821207676710864, 0.5392724012504414, 1.8065252817609618)), # Two tests with different inputs to show that input is ignored. (Functions.NormalDist, 1e14, {"mean": RAND1, "standard_deviation": RAND2}, None, (1.0890232855122397)), diff --git a/tests/json/model_with_control.py b/tests/json/model_with_control.py index eec85267387..105bbc1731f 100644 --- a/tests/json/model_with_control.py +++ b/tests/json/model_with_control.py @@ -27,7 +27,7 @@ ), ), noise=0.5, - starting_point=0, + starting_value=0, t0=0.45, ), output_ports=[ diff --git a/tests/log/test_log.py b/tests/log/test_log.py index 78c05c732aa..3d758e54ff2 100644 --- a/tests/log/test_log.py +++ b/tests/log/test_log.py @@ -1136,7 +1136,7 @@ def node_logged_in_simulation(self): control_signal_params={pnl.ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}) ), noise=0.5, - starting_point=0, + starting_value=0, t0=0.45 ), output_ports=[ diff --git a/tests/mechanisms/test_ddm_mechanism.py b/tests/mechanisms/test_ddm_mechanism.py index c7fbe07d51a..eb26f3e476e 100644 --- a/tests/mechanisms/test_ddm_mechanism.py +++ b/tests/mechanisms/test_ddm_mechanism.py @@ -744,7 +744,7 @@ def test_sequence_of_DDM_mechs_in_Composition_Pathway(): function=DriftDiffusionAnalytical( drift_rate=(1.0), threshold=(10.0), - starting_point=0.0, + starting_value=0.0, ), name='My_DDM', ) diff --git a/tests/mechanisms/test_drift_diffusion_analytical.py b/tests/mechanisms/test_drift_diffusion_analytical.py index e60615ef8a6..201cfe5addc 100644 --- a/tests/mechanisms/test_drift_diffusion_analytical.py +++ b/tests/mechanisms/test_drift_diffusion_analytical.py @@ -13,7 +13,7 @@ def check_drift_diffusion_analytical(B, data, degenerate_cases=False): Helper function to check a DriftDiffusionAnalytical Function against a set of data. Format of the data follows the following column ordering: - stim, drift_rate, threshold, starting_point, bias, t0, noise, mean ER, mean RT, + stim, drift_rate, threshold, starting_value, bias, t0, noise, mean ER, mean RT, correct RT mean, correct RT variance, correct RT skew See gen_matlab_ddm_test_data.py script to generate more test data in this form. This script has since @@ -25,12 +25,12 @@ def check_drift_diffusion_analytical(B, data, degenerate_cases=False): """ NUM_CHECKS = data.shape[0] for i in range(NUM_CHECKS): - r_stim, r_drift_rate, r_threshold, r_starting_point, r_bias, r_t0, r_noise = data[i, 0:7].tolist() + r_stim, r_drift_rate, r_threshold, r_starting_value, r_bias, r_t0, r_noise = data[i, 0:7].tolist() ground_truth = data[i,7:] B.function.drift_rate.base = r_drift_rate B.function.threshold.base = r_threshold - B.function.starting_point.base = r_starting_point + B.function.starting_value.base = r_starting_value B.function.t0.base = r_t0 B.function.noise.base = r_noise diff --git a/tests/scheduling/test_system_newsched.py b/tests/scheduling/test_system_newsched.py index 88afa82ee17..812550fb57d 100644 --- a/tests/scheduling/test_system_newsched.py +++ b/tests/scheduling/test_system_newsched.py @@ -39,7 +39,7 @@ def test_create_scheduler_from_system_StroopDemo(self): drift_rate=(1.0), threshold=(0.1654), noise=(0.5), - starting_point=(0), + starting_value=(0), t0=0.25, ), name='Decision', From cf492056c73ce4a2cbc67cba81a881eafea46c6e Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 9 Dec 2020 18:16:12 -0500 Subject: [PATCH 014/285] DriftDiffusionAnalytical: rename t0 to non_decision_time --- ...on_Reward_rate_with_penalty_with_inputs.py | 2 +- Scripts/Debug/StabilityFlexibility.py | 2 +- Scripts/Debug/Umemoto_Feb.py | 6 +-- Scripts/Debug/Umemoto_Feb2.py | 6 +-- Scripts/Debug/markus_test_umemoto.py | 6 +-- Scripts/Debug/new_umemoto.py | 6 +-- Scripts/Debug/stability_flexibility_simple.py | 2 +- Scripts/Examples/EVC OCM.py | 2 +- Scripts/Examples/EVC-Gratton Composition.py | 2 +- .../Examples/EVC-Gratton-GaussianProcess.py | 2 +- Scripts/Examples/StabilityFlexibility.py | 2 +- Scripts/Examples/Stroop Model.py | 2 +- .../Examples/Tutorial/Stroop Model - EVC.py | 2 +- .../Bustamante_Stroop_XOR_LVOC_Model_VZ.py | 2 +- .../ColorMotionTask_FULL.py | 2 +- .../ColorMotionTask_SIMPLE.py | 2 +- .../functions/distributionfunctions.py | 48 +++++++++---------- .../mechanisms/processing/integrator/ddm.py | 8 ++-- tests/composition/test_composition.py | 4 +- tests/composition/test_control.py | 14 +++--- tests/functions/test_distribution.py | 6 +-- tests/json/model_with_control.py | 2 +- tests/log/test_log.py | 2 +- .../test_drift_diffusion_analytical.py | 6 +-- tests/scheduling/test_system_newsched.py | 2 +- 25 files changed, 70 insertions(+), 70 deletions(-) diff --git a/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py b/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py index 169ac54c3c5..e26435129d6 100644 --- a/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py +++ b/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py @@ -121,7 +121,7 @@ def get_stroop_model(unit_noise_std=.01, dec_noise_std=.1): threshold =1, noise=1, starting_value=0, - t0=0.35), + non_decision_time=0.35), output_ports=[pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD, pnl.PROBABILITY_LOWER_THRESHOLD] diff --git a/Scripts/Debug/StabilityFlexibility.py b/Scripts/Debug/StabilityFlexibility.py index 2df05100a6d..3425f3a2b9c 100644 --- a/Scripts/Debug/StabilityFlexibility.py +++ b/Scripts/Debug/StabilityFlexibility.py @@ -126,7 +126,7 @@ def computeAccuracy(variable): starting_value = STARTING_VALUE, threshold = THRESHOLD, noise = NOISE, - t0 = T0), + non_decision_time = T0), output_ports = [pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD, pnl.PROBABILITY_LOWER_THRESHOLD], name='DDM') diff --git a/Scripts/Debug/Umemoto_Feb.py b/Scripts/Debug/Umemoto_Feb.py index 7259fd17e76..2bde0b20664 100644 --- a/Scripts/Debug/Umemoto_Feb.py +++ b/Scripts/Debug/Umemoto_Feb.py @@ -13,7 +13,7 @@ # EVC params for Umemoto et al -t0 = 0.2 +non_decision_time = 0.2 c = 0.19 thresh = 0.21 x_0 = 0 # starting point @@ -60,7 +60,7 @@ threshold=(thresh), noise=(c), starting_value=(x_0), - t0=t0 + non_decision_time=non_decision_time ),name='Decision', output_ports=[ pnl.DECISION_VARIABLE, @@ -71,7 +71,7 @@ pnl.VARIABLE: (pnl.OWNER_VALUE, 2), pnl.FUNCTION: pnl.Linear(0, slope=1.0, intercept=1) } - ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), t0=0.15 + ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), non_decision_time=0.15 Decision.set_log_conditions('InputPort-0')#, log_condition=pnl.PROCESSING) diff --git a/Scripts/Debug/Umemoto_Feb2.py b/Scripts/Debug/Umemoto_Feb2.py index 9d58c12b08c..615b5ade494 100644 --- a/Scripts/Debug/Umemoto_Feb2.py +++ b/Scripts/Debug/Umemoto_Feb2.py @@ -13,7 +13,7 @@ # EVC params for Umemoto et al -t0 = 0.2 +non_decision_time = 0.2 c = 0.19 thresh = 0.21 x_0 = 0 # starting point @@ -62,7 +62,7 @@ threshold=(thresh), noise=(c), starting_value=(x_0), - t0=t0 + non_decision_time=non_decision_time ),name='Decision', output_ports=[ pnl.DECISION_VARIABLE, @@ -73,7 +73,7 @@ pnl.VARIABLE: (pnl.OWNER_VALUE, 2), pnl.FUNCTION: pnl.Linear(0, slope=1.0, intercept=1) } - ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), t0=0.15 + ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), non_decision_time=0.15 Decision.set_log_conditions('InputPort-0')#, log_condition=pnl.PROCESSING) Decision.set_log_conditions('PROBABILITY_UPPER_THRESHOLD') diff --git a/Scripts/Debug/markus_test_umemoto.py b/Scripts/Debug/markus_test_umemoto.py index 5905194e0ba..b9920241e41 100644 --- a/Scripts/Debug/markus_test_umemoto.py +++ b/Scripts/Debug/markus_test_umemoto.py @@ -16,7 +16,7 @@ # EVC params for Umemoto et al -t0 = 0.2 +non_decision_time = 0.2 c = 0.19 thresh = 0.21 x_0 = 0 # starting point @@ -77,7 +77,7 @@ threshold=(thresh), noise=(c), starting_value=(x_0), - t0=t0 + non_decision_time=non_decision_time ),name='Decision', output_ports=[ pnl.DECISION_VARIABLE, @@ -88,7 +88,7 @@ pnl.VARIABLE: (pnl.OWNER_VALUE, 2), pnl.FUNCTION: psyneulink.core.components.functions.transferfunctions.Linear(0, slope=1.0, intercept=1) } - ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), t0=0.15 + ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), non_decision_time=0.15 print(Decision.execute([1])) diff --git a/Scripts/Debug/new_umemoto.py b/Scripts/Debug/new_umemoto.py index 685b4a9c869..b98205f1361 100644 --- a/Scripts/Debug/new_umemoto.py +++ b/Scripts/Debug/new_umemoto.py @@ -13,7 +13,7 @@ # EVC params for Umemoto et al -t0 = 0.2 +non_decision_time = 0.2 c = 0.19 thresh = 0.21 x_0 = 0 # starting point @@ -56,7 +56,7 @@ threshold=(thresh), noise=(c), starting_value=(x_0), - t0=t0 + non_decision_time=non_decision_time ),name='Decision', output_ports=[ pnl.DECISION_VARIABLE, @@ -67,7 +67,7 @@ pnl.VARIABLE: (pnl.OWNER_VALUE, 2), pnl.FUNCTION: pnl.Linear(0, slope=1.0, intercept=1) } - ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), t0=0.15 + ],) #drift_rate=(1.0),threshold=(0.2645),noise=(0.5),non_decision_time=(0), non_decision_time=0.15 Decision.set_log_conditions('InputPort-0') diff --git a/Scripts/Debug/stability_flexibility_simple.py b/Scripts/Debug/stability_flexibility_simple.py index 81752af3999..c620ead7ffc 100644 --- a/Scripts/Debug/stability_flexibility_simple.py +++ b/Scripts/Debug/stability_flexibility_simple.py @@ -119,7 +119,7 @@ def computeAccuracy(variable): starting_value=STARTING_VALUE, threshold=THRESHOLD, noise=NOISE, - t0=T0), + non_decision_time=T0), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD, pnl.PROBABILITY_LOWER_THRESHOLD], diff --git a/Scripts/Examples/EVC OCM.py b/Scripts/Examples/EVC OCM.py index 4c0882b0da9..265194ccc8c 100644 --- a/Scripts/Examples/EVC OCM.py +++ b/Scripts/Examples/EVC OCM.py @@ -17,7 +17,7 @@ np.arange(0.1, 1.01, 0.3)})), noise=0.5, starting_value=0, - t0=0.45), + non_decision_time=0.45), output_ports=[DECISION_VARIABLE, RESPONSE_TIME, PROBABILITY_UPPER_THRESHOLD], diff --git a/Scripts/Examples/EVC-Gratton Composition.py b/Scripts/Examples/EVC-Gratton Composition.py index fc900390543..2c035b1df81 100644 --- a/Scripts/Examples/EVC-Gratton Composition.py +++ b/Scripts/Examples/EVC-Gratton Composition.py @@ -19,7 +19,7 @@ threshold=(0.2645), noise=(0.5), starting_value=(0), - t0=0.15), + non_decision_time=0.15), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD] diff --git a/Scripts/Examples/EVC-Gratton-GaussianProcess.py b/Scripts/Examples/EVC-Gratton-GaussianProcess.py index 11d9f5a0f4c..de9730b660f 100644 --- a/Scripts/Examples/EVC-Gratton-GaussianProcess.py +++ b/Scripts/Examples/EVC-Gratton-GaussianProcess.py @@ -35,7 +35,7 @@ ), noise=(0.5), starting_value=(0), - t0=0.45 + non_decision_time=0.45 ), output_ports=[ pnl.DECISION_VARIABLE, diff --git a/Scripts/Examples/StabilityFlexibility.py b/Scripts/Examples/StabilityFlexibility.py index 41686e7a94e..b9552eba0e7 100644 --- a/Scripts/Examples/StabilityFlexibility.py +++ b/Scripts/Examples/StabilityFlexibility.py @@ -127,7 +127,7 @@ def computeAccuracy(variable): starting_value = STARTING_VALUE, threshold = THRESHOLD, noise = NOISE, - t0 = T0), + non_decision_time = T0), output_ports = [pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD, pnl.PROBABILITY_LOWER_THRESHOLD], name='DDM') diff --git a/Scripts/Examples/Stroop Model.py b/Scripts/Examples/Stroop Model.py index 0e916ad5c70..982fb67cec1 100644 --- a/Scripts/Examples/Stroop Model.py +++ b/Scripts/Examples/Stroop Model.py @@ -33,7 +33,7 @@ threshold=(0.2645), noise=(0.5), starting_value=(0), - t0=0.15), + non_decision_time=0.15), output_ports=[DECISION_VARIABLE, RESPONSE_TIME, PROBABILITY_UPPER_THRESHOLD] diff --git a/Scripts/Examples/Tutorial/Stroop Model - EVC.py b/Scripts/Examples/Tutorial/Stroop Model - EVC.py index 053e559e82c..d4fab5fde26 100644 --- a/Scripts/Examples/Tutorial/Stroop Model - EVC.py +++ b/Scripts/Examples/Tutorial/Stroop Model - EVC.py @@ -33,7 +33,7 @@ threshold=(0.2645), noise=(0.5), starting_value=(0), - t0=0.15), + non_decision_time=0.15), output_ports=[DECISION_VARIABLE, RESPONSE_TIME, PROBABILITY_UPPER_THRESHOLD] diff --git a/Scripts/Models (Under Development)/Bustamante_Stroop_XOR_LVOC_Model_VZ.py b/Scripts/Models (Under Development)/Bustamante_Stroop_XOR_LVOC_Model_VZ.py index 2a840a1c6a7..e2f5aac32bb 100644 --- a/Scripts/Models (Under Development)/Bustamante_Stroop_XOR_LVOC_Model_VZ.py +++ b/Scripts/Models (Under Development)/Bustamante_Stroop_XOR_LVOC_Model_VZ.py @@ -93,7 +93,7 @@ def adj_cost_fct(v): function=pnl.DriftDiffusionAnalytical( threshold=2.27, noise=0.4, - t0=.4 + non_decision_time=.4 ), output_ports=[ pnl.PROBABILITY_UPPER_THRESHOLD, diff --git a/Scripts/Models (Under Development)/ColorMotionTask_FULL.py b/Scripts/Models (Under Development)/ColorMotionTask_FULL.py index b0bbfc7e1e4..74447feaf8d 100644 --- a/Scripts/Models (Under Development)/ColorMotionTask_FULL.py +++ b/Scripts/Models (Under Development)/ColorMotionTask_FULL.py @@ -124,7 +124,7 @@ def incongruent(colorResponse, motionResponse): function=DriftDiffusionAnalytical( starting_value=0, noise=0.5, - t0=0.2, + non_decision_time=0.2, threshold=0.45), output_ports=[PROBABILITY_UPPER_THRESHOLD, RESPONSE_TIME], ) diff --git a/Scripts/Models (Under Development)/ColorMotionTask_SIMPLE.py b/Scripts/Models (Under Development)/ColorMotionTask_SIMPLE.py index 9fa8567715c..445a500d562 100644 --- a/Scripts/Models (Under Development)/ColorMotionTask_SIMPLE.py +++ b/Scripts/Models (Under Development)/ColorMotionTask_SIMPLE.py @@ -11,7 +11,7 @@ function=DriftDiffusionAnalytical( starting_value=0, noise=0.5, - t0=0.2, + non_decision_time=0.2, threshold=0.45), output_ports=[PROBABILITY_UPPER_THRESHOLD, RESPONSE_TIME], ) diff --git a/psyneulink/core/components/functions/distributionfunctions.py b/psyneulink/core/components/functions/distributionfunctions.py index 846f37fb1d8..b2ccbcc9f6b 100644 --- a/psyneulink/core/components/functions/distributionfunctions.py +++ b/psyneulink/core/components/functions/distributionfunctions.py @@ -914,7 +914,7 @@ def _function(self, THRESHOLD_VARIABILITY = 'DDM_ThresholdRateVariability' STARTING_VALUE = 'starting_value' STARTING_VALUE_VARIABILITY = "DDM_StartingPointVariability" -NON_DECISION_TIME = 't0' +NON_DECISION_TIME = 'non_decision_time' def _DriftDiffusionAnalytical_bias_getter(owning_component=None, context=None): @@ -934,7 +934,7 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- drift_rate=1.0, \ threshold=1.0, \ starting_value=0.0, \ - t0=0.2 \ + non_decision_time=0.2 \ noise=0.5, \ params=None, \ owner=None, \ @@ -976,7 +976,7 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- If it is a float, it must be a number from 0 to 1. If it is a list or array, it must be the same length as `default_variable ` and all elements must be floats from 0 to 1. - t0 : float, list or 1d array : default 0.2 + non_decision_time : float, list or 1d array : default 0.2 specifies the non-decision time for solution. If it is a float, it must be a number from 0 to 1. If it is a list or array, it must be the same length as `default_variable ` and all elements must be floats from 0 to 1. @@ -1020,7 +1020,7 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- determines the diffusion component of the drift diffusion process (used to specify the variance of a Gaussian random process). - t0 : float or 1d array + non_decision_time : float or 1d array determines the assumed non-decision time to determine the response time returned by the solution. bias : float or 1d array @@ -1081,8 +1081,8 @@ class Parameters(DistributionFunction.Parameters): :default value: 0.0 :type: ``float`` - t0 - see `t0 ` + non_decision_time + see `non_decision_time ` :default value: 0.2 :type: ``float`` @@ -1097,7 +1097,7 @@ class Parameters(DistributionFunction.Parameters): starting_value = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) threshold = Parameter(1.0, modulable=True) noise = Parameter(0.5, modulable=True) - t0 = Parameter(.200, modulable=True) + non_decision_time = Parameter(.200, modulable=True) bias = Parameter(0.5, read_only=True, getter=_DriftDiffusionAnalytical_bias_getter) # this is read only because conversion is disabled for this function # this occurs in other places as well @@ -1116,7 +1116,7 @@ def __init__(self, starting_value: tc.optional(parameter_spec) = None, threshold: tc.optional(parameter_spec) = None, noise: tc.optional(parameter_spec) = None, - t0: tc.optional(parameter_spec) = None, + non_decision_time: tc.optional(parameter_spec) = None, params=None, owner=None, prefs: tc.optional(is_pref_set) = None, @@ -1130,7 +1130,7 @@ def __init__(self, starting_value=starting_value, threshold=threshold, noise=noise, - t0=t0, + non_decision_time=non_decision_time, params=params, owner=owner, prefs=prefs, @@ -1210,13 +1210,13 @@ def _function(self, threshold = self._get_current_parameter_value(THRESHOLD, context) starting_value = float(self._get_current_parameter_value(STARTING_VALUE, context)) noise = float(self._get_current_parameter_value(NOISE, context)) - t0 = float(self._get_current_parameter_value(NON_DECISION_TIME, context)) + non_decision_time = float(self._get_current_parameter_value(NON_DECISION_TIME, context)) # drift_rate = float(self.drift_rate) * float(variable) # threshold = float(self.threshold) # starting_value = float(self.starting_value) # noise = float(self.noise) - # t0 = float(self.t0) + # non_decision_time = float(self.non_decision_time) bias = (starting_value + threshold) / (2 * threshold) @@ -1231,7 +1231,7 @@ def _function(self, # back to absolute bias in order to apply limit bias_abs = bias * 2 * threshold - threshold # use expression for limit a->0 from Srivastava et al. 2016 - rt = t0 + (threshold ** 2 - bias_abs ** 2) / (noise ** 2) + rt = non_decision_time + (threshold ** 2 - bias_abs ** 2) / (noise ** 2) er = (threshold - bias_abs) / (2 * threshold) else: drift_rate_normed = np.abs(drift_rate) @@ -1285,7 +1285,7 @@ def _function(self, if rt < 0: rt = 0 - rt = rt + t0 + rt = rt + non_decision_time except FloatingPointError: # Per Mike Shvartsman: @@ -1294,7 +1294,7 @@ def _function(self, # depending on the sign of the drift, and so decision time goes to a point mass on z/a – x0, and # generates a "RuntimeWarning: overflow encountered in exp" er = 0 - rt = ztilde / atilde - x0tilde + t0 + rt = ztilde / atilde - x0tilde + non_decision_time # This last line makes it report back in terms of a fixed reference point # (i.e., closer to 1 always means higher p(upper boundary)) @@ -1303,14 +1303,14 @@ def _function(self, er = (is_neg_drift == 1) * (1 - er) + (is_neg_drift == 0) * (er) # Compute moments (mean, variance, skew) of condiational response time distributions - moments = DriftDiffusionAnalytical._compute_conditional_rt_moments(drift_rate, noise, threshold, bias, t0) + moments = DriftDiffusionAnalytical._compute_conditional_rt_moments(drift_rate, noise, threshold, bias, non_decision_time) return rt, er, \ moments['mean_rt_plus'], moments['var_rt_plus'], moments['skew_rt_plus'], \ moments['mean_rt_minus'], moments['var_rt_minus'], moments['skew_rt_minus'] @staticmethod - def _compute_conditional_rt_moments(drift_rate, noise, threshold, starting_value, t0): + def _compute_conditional_rt_moments(drift_rate, noise, threshold, starting_value, non_decision_time): """ This is a helper function for computing the conditional decison time moments for the DDM. It is based completely off of Matlab\\DDMFunctions\\ddm_metrics_cond_Mat.m. @@ -1319,7 +1319,7 @@ def _compute_conditional_rt_moments(drift_rate, noise, threshold, starting_value :param noise: The diffusion rate. :param threshold: The symmetric threshold of the DDM :param starting_value: The initial condition. - :param t0: The non decision time. + :param non_decision_time: The non decision time. :return: A dictionary containing the following key value pairs: mean_rt_plus: The mean RT of positive responses. mean_rt_minus: The mean RT of negative responses. @@ -1380,8 +1380,8 @@ def csch(x): moments['skew_rt_minus'] /= moments['var_rt_minus']**1.5 # Add the non-decision time to the mean RTs - moments['mean_rt_plus'] += t0 - moments['mean_rt_minus'] += t0 + moments['mean_rt_plus'] += non_decision_time + moments['mean_rt_minus'] += non_decision_time return moments @@ -1396,7 +1396,7 @@ def load_scalar_param(name): threshold = load_scalar_param(THRESHOLD) starting_value = load_scalar_param(STARTING_VALUE) noise = load_scalar_param(NOISE) - t0 = load_scalar_param(NON_DECISION_TIME) + non_decision_time = load_scalar_param(NON_DECISION_TIME) noise_sqr = builder.fmul(noise, noise) @@ -1437,7 +1437,7 @@ def _get_arg_out_ptr(idx): threshold_sqr = builder.fmul(threshold, threshold) rt = builder.fsub(threshold_sqr, bias_abs_sqr) rt = builder.fdiv(rt, noise_sqr) - rt = builder.fadd(t0, rt) + rt = builder.fadd(non_decision_time, rt) builder.store(rt, rt_ptr) er = builder.fsub(threshold, bias_abs) @@ -1517,7 +1517,7 @@ def _get_arg_out_ptr(idx): rt = builder.fdiv(rt_tmp1, rt_tmp2) rt = builder.fsub(rt, x0tilde) rt = builder.fadd(rt_tmp0, rt) - rt = builder.fadd(rt, t0) + rt = builder.fadd(rt, non_decision_time) builder.store(rt, rt_ptr) # Calculate moments @@ -1566,14 +1566,14 @@ def _get_arg_out_ptr(idx): mrtp_tmp = builder.fsub(Z2_coth_Z2, ZpX_coth_ZpX) m_rt_p = builder.fdiv(noise_sqr, drift_rate_sqr) m_rt_p = builder.fmul(m_rt_p, mrtp_tmp) - m_rt_p = builder.fadd(m_rt_p, t0) + m_rt_p = builder.fadd(m_rt_p, non_decision_time) builder.store(m_rt_p, mean_rt_plus_ptr) # Mean minus mrtm_tmp = builder.fsub(Z2_coth_Z2, ZmX_coth_ZmX) m_rt_m = builder.fdiv(noise_sqr, drift_rate_sqr) m_rt_m = builder.fmul(m_rt_m, mrtm_tmp) - m_rt_m = builder.fadd(m_rt_m, t0) + m_rt_m = builder.fadd(m_rt_m, non_decision_time) builder.store(m_rt_m, mean_rt_minus_ptr) # Variance helpers diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index e7f8645d2ba..bfb1aa04043 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -208,7 +208,7 @@ ... starting_value=0.5, ... threshold=0.2645, ... noise=0.5, - ... t0=0.15 + ... non_decision_time=0.15 ... ), ... name='my_DDM_DriftDiffusionAnalytical' ... ) @@ -292,7 +292,7 @@ - specifies the variance of the stochastic ("diffusion") component of the decision process. .. * `NON_DECISION_TIME` (default 0.2) - specifies the `t0` parameter of the decision process (in units of seconds). + specifies the `non_decision_time` parameter of the decision process (in units of seconds). [TBI - MULTIPROCESS DDM - REPLACE BELOW] When a DDM Mechanism is executed it computes the decision process, either analytically (in TRIAL mode) @@ -315,7 +315,7 @@ created) for the next execution. ADD NOTE ABOUT INTERROGATION PROTOCOL, USING ``terminate_function`` - ADD NOTE ABOUT RELATIONSHIP OF RT TO time_steps TO t0 TO ms + ADD NOTE ABOUT RELATIONSHIP OF RT TO time_steps TO non_decision_time TO ms COMMENT .. _DDM_Execution: @@ -710,7 +710,7 @@ class Parameters(ProcessingMechanism.Parameters): starting_value=0.0, threshold=1.0, noise=0.5, - t0=.200, + non_decision_time=.200, ), stateful=False, loggable=False diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 48f718ab384..c29e461904d 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -7066,7 +7066,7 @@ def test_danglingControlledMech(self): threshold=(0.1654), noise=(0.5), starting_value=(0), - t0=0.25, + non_decision_time=0.25, ), name='Decision', ) @@ -7096,7 +7096,7 @@ def test_danglingControlledMech(self): ), noise=(0.5), starting_value=(0), - t0=0.45 + non_decision_time=0.45 ), name='second_DDM', ) diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 13bc8e732a1..9e0213176ef 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -135,7 +135,7 @@ def test_deferred_init(self): 0.3)})), noise=0.5, starting_value=0, - t0=0.45), + non_decision_time=0.45), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD], @@ -829,7 +829,7 @@ def test_two_tier_ocm(self): starting_value=STARTING_VALUE, threshold=THRESHOLD, noise=NOISE, - t0=T0), + non_decision_time=T0), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD, pnl.PROBABILITY_LOWER_THRESHOLD], @@ -1164,7 +1164,7 @@ def test_evc(self): control_signal_params={pnl.ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)})), noise=0.5, starting_value=0, - t0=0.45), + non_decision_time=0.45), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD], @@ -1272,7 +1272,7 @@ def test_evc_gratton(self): threshold=(0.2645), noise=(0.5), starting_value=(0), - t0=0.15), + non_decision_time=0.15), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD] @@ -1443,7 +1443,7 @@ def test_laming_validation_specify_control_signals(self): threshold=1.0, noise=0.5, starting_value=0, - t0=0.45 + non_decision_time=0.45 ), output_ports=[ pnl.DECISION_VARIABLE, @@ -1581,7 +1581,7 @@ def test_stateful_mechanism_in_simulation(self): ), noise=(0.5), starting_value=(0), - t0=0.45 + non_decision_time=0.45 ), output_ports=[ pnl.DECISION_VARIABLE, @@ -1948,7 +1948,7 @@ def computeAccuracy(trialInformation): starting_value=STARTING_VALUE, threshold=THRESHOLD, noise=NOISE, - t0=T0), + non_decision_time=T0), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME, pnl.PROBABILITY_UPPER_THRESHOLD, pnl.PROBABILITY_LOWER_THRESHOLD], diff --git a/tests/functions/test_distribution.py b/tests/functions/test_distribution.py index 073e77558e7..e1f4e8a49d3 100644 --- a/tests/functions/test_distribution.py +++ b/tests/functions/test_distribution.py @@ -16,12 +16,12 @@ test_data = [ (Functions.DriftDiffusionAnalytical, test_var, {}, None, (1.9774974807292212, 0.012242689689501842, 1.9774974807292207, 1.3147677945132479, 1.7929299891370192, 1.9774974807292207, 1.3147677945132479, 1.7929299891370192)), - (Functions.DriftDiffusionAnalytical, test_var, {"drift_rate": RAND1, "threshold": RAND2, "starting_value": RAND3, "t0":RAND4, "noise": RAND5}, None, + (Functions.DriftDiffusionAnalytical, test_var, {"drift_rate": RAND1, "threshold": RAND2, "starting_value": RAND3, "non_decision_time":RAND4, "noise": RAND5}, None, (0.4236547993389047, -2.7755575615628914e-17, 0.5173675420165031, 0.06942854144616283, 6.302631815990666, 1.4934079600147951, 0.4288991185241868, 1.7740760781361433)), - (Functions.DriftDiffusionAnalytical, -test_var, {"drift_rate": RAND1, "threshold": RAND2, "starting_value": RAND3, "t0":RAND4, "noise": RAND5}, None, + (Functions.DriftDiffusionAnalytical, -test_var, {"drift_rate": RAND1, "threshold": RAND2, "starting_value": RAND3, "non_decision_time":RAND4, "noise": RAND5}, None, (0.42365479933890504, 0.0, 0.5173675420165031, 0.06942854144616283, 6.302631815990666, 1.4934079600147951, 0.4288991185241868, 1.7740760781361433)), # FIXME: Rounding errors result in different behaviour on different platforms -# (Functions.DriftDiffusionAnalytical, 1e-4, {"drift_rate": 1e-5, "threshold": RAND2, "starting_value": RAND3, "t0":RAND4, "noise": RAND5}, "Rounding errors", +# (Functions.DriftDiffusionAnalytical, 1e-4, {"drift_rate": 1e-5, "threshold": RAND2, "starting_value": RAND3, "non_decision_time":RAND4, "noise": RAND5}, "Rounding errors", # (0.5828813465336954, 0.04801236718458773, 0.532471083815943, 0.09633801362499317, 6.111833139205608, 1.5821207676710864, 0.5392724012504414, 1.8065252817609618)), # Two tests with different inputs to show that input is ignored. (Functions.NormalDist, 1e14, {"mean": RAND1, "standard_deviation": RAND2}, None, (1.0890232855122397)), diff --git a/tests/json/model_with_control.py b/tests/json/model_with_control.py index 105bbc1731f..2b32762103e 100644 --- a/tests/json/model_with_control.py +++ b/tests/json/model_with_control.py @@ -28,7 +28,7 @@ ), noise=0.5, starting_value=0, - t0=0.45, + non_decision_time=0.45, ), output_ports=[ pnl.DECISION_VARIABLE, diff --git a/tests/log/test_log.py b/tests/log/test_log.py index 3d758e54ff2..418dd2b4d57 100644 --- a/tests/log/test_log.py +++ b/tests/log/test_log.py @@ -1137,7 +1137,7 @@ def node_logged_in_simulation(self): ), noise=0.5, starting_value=0, - t0=0.45 + non_decision_time=0.45 ), output_ports=[ pnl.DECISION_VARIABLE, diff --git a/tests/mechanisms/test_drift_diffusion_analytical.py b/tests/mechanisms/test_drift_diffusion_analytical.py index 201cfe5addc..796a3079dcf 100644 --- a/tests/mechanisms/test_drift_diffusion_analytical.py +++ b/tests/mechanisms/test_drift_diffusion_analytical.py @@ -13,7 +13,7 @@ def check_drift_diffusion_analytical(B, data, degenerate_cases=False): Helper function to check a DriftDiffusionAnalytical Function against a set of data. Format of the data follows the following column ordering: - stim, drift_rate, threshold, starting_value, bias, t0, noise, mean ER, mean RT, + stim, drift_rate, threshold, starting_value, bias, non_decision_time, noise, mean ER, mean RT, correct RT mean, correct RT variance, correct RT skew See gen_matlab_ddm_test_data.py script to generate more test data in this form. This script has since @@ -25,13 +25,13 @@ def check_drift_diffusion_analytical(B, data, degenerate_cases=False): """ NUM_CHECKS = data.shape[0] for i in range(NUM_CHECKS): - r_stim, r_drift_rate, r_threshold, r_starting_value, r_bias, r_t0, r_noise = data[i, 0:7].tolist() + r_stim, r_drift_rate, r_threshold, r_starting_value, r_bias, r_non_decision_time, r_noise = data[i, 0:7].tolist() ground_truth = data[i,7:] B.function.drift_rate.base = r_drift_rate B.function.threshold.base = r_threshold B.function.starting_value.base = r_starting_value - B.function.t0.base = r_t0 + B.function.non_decision_time.base = r_non_decision_time B.function.noise.base = r_noise results_b = B.execute(r_stim) diff --git a/tests/scheduling/test_system_newsched.py b/tests/scheduling/test_system_newsched.py index 812550fb57d..4aaa197307e 100644 --- a/tests/scheduling/test_system_newsched.py +++ b/tests/scheduling/test_system_newsched.py @@ -40,7 +40,7 @@ def test_create_scheduler_from_system_StroopDemo(self): threshold=(0.1654), noise=(0.5), starting_value=(0), - t0=0.25, + non_decision_time=0.25, ), name='Decision', ) From af14f746b1fbc34979ee9ad4d5f9a266e00841b3 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 19 Jan 2021 17:23:27 -0500 Subject: [PATCH 015/285] Modified default DDM behaviour to reset sate AtTrialStart --- Scripts/Debug/ddm/__init__.py | 0 Scripts/Debug/ddm/ddm_plot_check.py | 179 ++++++++++++++++++ Scripts/Debug/ddm/wfpt.py | 174 +++++++++++++++++ .../mechanisms/processing/integrator/ddm.py | 14 +- 4 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 Scripts/Debug/ddm/__init__.py create mode 100644 Scripts/Debug/ddm/ddm_plot_check.py create mode 100644 Scripts/Debug/ddm/wfpt.py diff --git a/Scripts/Debug/ddm/__init__.py b/Scripts/Debug/ddm/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py new file mode 100644 index 00000000000..a8d3887d85a --- /dev/null +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -0,0 +1,179 @@ +#%% +import numpy as np +import psyneulink as pnl + +import matplotlib.pyplot as plt +plt.rcParams["figure.figsize"] = (20,10) + +import matplotlib.pyplot as plt +import seaborn as sns +import pandas as pd + +import wfpt + +from psyneulink.core.components.functions.fitfunctions import simulation_likelihood + + +def ddm_pdf_analytical(drift_rate, threshold, noise, starting_point, non_decision_time, time_step_size=0.01): + from ddm import Model + from ddm.models import DriftConstant, NoiseConstant, BoundConstant, OverlayNonDecision, ICPoint + from ddm.functions import display_model + + model = Model(name='Simple model', + drift=DriftConstant(drift=drift_rate), + noise=NoiseConstant(noise=noise), + bound=BoundConstant(B=threshold), + IC=ICPoint(x0=starting_point), + overlay=OverlayNonDecision(nondectime=non_decision_time), + dx=.001, dt=time_step_size, T_dur=3) + display_model(model) + s = model.solve() + + return model.t_domain(), s.pdf_corr(), s.pdf_err() + + +def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0.0, non_decision_time=0.0, + time_step_size=0.01, num_samples=1000000, use_pnl=True, rt_space=None): + + if use_pnl: + decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_value=0.8, + rate=-0.75, + threshold=0.8, + noise=0.1, + time_step_size=0.01), + output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], + name='DDM') + + comp = pnl.Composition() + comp.add_node(decision) + + comp.termination_processing = {pnl.TimeScale.TRIAL: pnl.WhenFinished(decision)} + + context = pnl.Context() + decision.function.parameters.rate.set(drift_rate, context) + decision.function.parameters.noise.set(noise, context) + decision.function.parameters.threshold.set(threshold, context) + decision.function.parameters.time_step_size.set(time_step_size, context) + decision.function.parameters.starting_value.set(np.array([starting_point]), context) + decision.function.parameters.time_step_size.set(time_step_size, context) + decision.function.parameters.non_decision_time.set(non_decision_time, context) + + input = np.ones((1, 1)) + + comp.run(inputs={decision: input}, + num_trials=num_samples * len(input), + bin_execute=True, + context=context) + + results = np.squeeze(np.array(comp.results)) + rts = np.array(np.vsplit(results, len(input))) + + else: + rts = wfpt.simulate_wfpt(starting_point * np.ones(num_samples), + non_decision_time * np.ones(num_samples), + drift_rate * np.ones(num_samples), + threshold * np.ones(num_samples), + dt=time_step_size) + rts = np.expand_dims(np.column_stack((np.sign(rts)*threshold, np.abs(rts))), axis=0) + + # Make a histogram + # hist = bh.Histogram(bh.axis.Boolean(), bh.axis.Regular(int(3 / time_step_size), 0.0, 3.0)) + # hist.fill(rts[:, :, 0].flatten() > 0.0, rts[:, :, 1].flatten()) + + if rt_space is None: + rt_space = np.linspace(0.0, 3.0, 3000) + + df = pd.DataFrame(index=rt_space) + + df[f'Correct KDE (dt={time_step_size})'] = simulation_likelihood(rts, + categorical_dims=np.array([True, False]), + combine_trials=True, + exp_data=np.c_[ + threshold * np.ones(len(rt_space)), rt_space]) + df[f'Error KDE (dt={time_step_size})'] = simulation_likelihood(rts, + categorical_dims=np.array([True, False]), + combine_trials=True, + exp_data=np.c_[ + -threshold * np.ones(len(rt_space)), rt_space]) + + #df[f'Correct Histogram (dt={time_step_size})'] = (hist[True, :] / hist.sum(flow=True) / time_step_size).view() + #df[f'Error Histogram (dt={time_step_size})'] = (hist[False, :] / hist.sum(flow=True) / time_step_size).view() + + return df + + +def main(): + + # from numpy.random import rand + # pd.DataFrame({ + # 'drift_rate': (rand() - .5) * 8, + # 'non_decision_time': 0.2 + rand() * 0.3, + # 'threshold': 0.5 + rand() * 1.5, + # 'noise': 1.0 + # }) + + ddm_params = dict(starting_point=0.0, drift_rate=0.1, noise=1.0, threshold=0.5, non_decision_time=0.0) + + NUM_SAMPLES = 10000 + + rt_space = np.linspace(0.0, 3.0, 30000) + + # Get the analytical + t_domain, pdf_corr, pdf_err = ddm_pdf_analytical(**ddm_params) + + # Interpolate to common rt space + from scipy.interpolate import interpn + anal_df = pd.DataFrame(index=rt_space) + anal_df[f"Correct Analytical"] = interpn((t_domain,), pdf_corr, rt_space, + method='linear', bounds_error=False, fill_value=1e-10) + anal_df[f"Error Analytical"] = interpn((t_domain,), pdf_err, rt_space, + method='linear', bounds_error=False, fill_value=1e-10) + + # Navarro and Fuss solution + # p_err = np.array( + # [wfpt.wfpt_logp(t, 0, starting_point, non_decision_time, drift_rate, threshold, eps=1e-10) for t in + # model_t_domain[1:]]) + # p_corr = np.array( + # [wfpt.wfpt_logp(t, 1, starting_point, non_decision_time, drift_rate, threshold, eps=1e-10) for t in + # model_t_domain[1:]]) + + df = pd.concat([ + anal_df, + ddm_pdf_simulate(**ddm_params, time_step_size=0.01, use_pnl=True, num_samples=NUM_SAMPLES, rt_space=rt_space), + #ddm_pdf_simulate(**ddm_params, time_step_size=0.001, use_pnl=True, num_samples=NUM_SAMPLES, rt_space=rt_space), + #ddm_pdf_simulate(**ddm_params, time_step_size=0.0001, use_pnl=True, num_samples=NUM_SAMPLES, rt_space=rt_space), + ]) + + fig, axes = plt.subplots(1, 2, sharex=True, sharey=True) + + #df = df.loc[:, ~df.columns.str.contains('Histogram')] + sns.lineplot(data=df.filter(regex='Correct'), ax=axes[0]) + sns.lineplot(data=df.filter(regex='Error'), ax=axes[1]) + plt.show() + + plt.savefig(f"{'_'.join([f'{p}={v}' for p,v in ddm_params.items()])}.png") + + # # Create a likelihood function from the composition itself, this is done + # # using probability density approximation via kernel density estimation. + # likelihood_func, param_map = make_likelihood_function(composition=comp, + # fit_params=[decision.function.parameters.rate, + # decision.function.parameters.starting_point], + # inputs=input, + # categorical_dims=np.array([True, False]), + # data_to_fit=data_to_fit, + # num_simulations=1000, + # fixed_params=dict( + # threshold=1.0, + # noise=0.1, + # time_step_size=0.01 + # ), + # combine_trials=True) + # + # params_to_fit = { + # 'rate': (0.0, 1.0), + # 'starting_point': (0.001, 1.0), + # } + + +if __name__ == "__main__": + main() diff --git a/Scripts/Debug/ddm/wfpt.py b/Scripts/Debug/ddm/wfpt.py new file mode 100644 index 00000000000..20fafa9bd1e --- /dev/null +++ b/Scripts/Debug/ddm/wfpt.py @@ -0,0 +1,174 @@ +import numpy as np +import math +import logging +from numpy import exp, log, sin, sqrt, pi, tanh, cosh, sinh + +logger = logging.getLogger(__name__) + + +def coth(x): + """ + Hyperbolic cotangent, coth(x) = cosh(x) / sinh(x) + """ + return cosh(x) / sinh(x) + + +def __stdWfptLargeTime(t, w, nterms): + # large time expansion from navarro & fuss + logger.debug("Large time expansion, %i terms" % nterms) + piSqOv2 = 4.93480220054 + + terms = pi * np.fromiter((k * exp(-k ** 2 * t * piSqOv2) * sin(k * pi * w) for k in range(1, nterms)), np.float64) + + return np.sum(terms) + + +def __stdWfptSmallTime(t, w, nterms): + # small time expansion navarro & fuss (todo: gondan et al. improvement) + logger.debug("Small time expansion, %i terms" % nterms) + + fr = -math.floor((nterms - 1) / 2) + to = math.ceil((nterms - 1) / 2) + + terms = 1 / sqrt(2 * pi * t ** 3) * np.fromiter( + ((w + 2 * k) * exp(-((w + 2 * k) ** 2) / (2 * t)) for k in range(fr, to)), np.float64) + return np.sum(terms) + + +def wfpt_logp(t, c, x0, t0, a, z, eps=1e-10): + """ + Log probability of first passage time of double-threshold wiener process + (aka "pure DDM" of Bogacz et al.). Uses series truncation of Navarro & Fuss 2009 + """ + + # boundary sep is 2 * thresh + boundarySep = 2 * z + # initial condition is absolute, so to make it relative divide by boundarySep + relativeInitCond = ((x0 + z) / boundarySep) + # normalize time + normT = (t - t0) / (boundarySep ** 2) + + # if t is below NDT, or x0 is outside bounds, probability of any rt is 0 + if normT < 0 or x0 > z or x0 < -z: + return -np.inf + + if c == 1: # by default return hit of lower bound, so if resp is correct flip + a = -a + relativeInitCond = 1 - relativeInitCond + + largeK = np.int(sqrt((-2 * log(pi * normT * eps)) / (pi ** 2 * normT))) + smallK = 2 + sqrt(-2 * normT * log(2 * eps * sqrt(2 * pi * normT))) + if largeK < smallK: + # make sure eps is small enough for for bound to be valid + if eps > (1 / (2 * sqrt(2 * pi * normT))): + smallK = 2 + p = __stdWfptLargeTime(normT, relativeInitCond, math.ceil(largeK)) + else: + # make sure eps is small enough for for bound to be valid + if eps > (1 / (pi * sqrt(normT))): + smallK = math.ceil((1 / (pi * sqrt(t)))) + p = __stdWfptSmallTime(normT, relativeInitCond, math.ceil(smallK)) + + # scale from the std case to whatever is our actual + scaler = (1 / boundarySep ** 2) * exp(-a * boundarySep * relativeInitCond - (a ** 2 * t / 2)) + + return scaler * p + #return log(scaler * p) + + +def __standardize_bogacz(x0, a, z, s): + """ + Standardize the way Bogacz et al. 2006 do: + threshold/drift ratio, signal to noise ratio, x0 to drift ratio + """ + ztilde = z / a + atilde = (a / s) ** 2 + x0tilde = x0 / a + return ztilde, atilde, x0tilde + + +def __standardize_srivastava(x0, a, z, s): + """ + Standardize the way Srivastava et al. (submitted) do: + normalized threshold, normalized starting point + """ + kz = (a * z) / (s * s) + kx = (a * x0) / (s * s) + return kz, kx + + +def __simulate_wfpt_single(x0, t0, a, z, dt): + particle = x0 + t = 0; + while abs(particle) < z: + particle = particle + np.random.normal(loc=a * dt, scale=sqrt(dt)) + t = t + 1 + return t0 + t * dt if particle > z else -t0 - t * dt + + +def simulate_wfpt(x0, t0, a, z, dt=0.01): + """ + Draws from the Wiener first passage time. Slow and imprecise, + used primarily for testing. Production usage of this function + is not recommended. + """ + # promote if we get scalars (probably not the best way to do this) + x0 = np.atleast_1d(x0) + t0 = np.atleast_1d(t0) + a = np.atleast_1d(a) + z = np.atleast_1d(z) + + return np.fromiter((__simulate_wfpt_single(_x0, _t0, _a, _z, dt) for _x0, _t0, _a, _z in zip(x0, t0, a, z)), + np.float64) + + +def wfpt_rt(x0, t0, a, z, s=1): + """ + Expected first passage time of a two-boundary wiener process. + Uses Bogacz et al. 2006 expression for nonzero drift, + Srivastava et al. expression for zero-drift. + """ + if abs(a) < 1e-8: # a close to 0 (avoid float comparison) + # use expression for limit a->0 from Srivastava et al. 2016 + return t0 + (z ** 2 - x0 ** 2) / (s ** 2) + # expression from Bogacz et al. 2006 for nonzero drift + else: + ztilde, atilde, x0tilde = __standardize_bogacz(x0, a, z, s) + return ztilde * tanh(ztilde * atilde) + ((2 * ztilde * (1 - exp(-2 * x0tilde * atilde))) / ( + exp(2 * ztilde * atilde) - exp(-2 * ztilde * atilde)) - x0tilde) + t0 + + +def wfpt_er(x0, t0, a, z, s=1): + """ + Crossing probability in the -drift direction (aka "Error rate") for wiener process. + Uses Bogacz et al. 2006 expression for nonzero drift, Srivastava et al. + expression for zero-drift. + """ + if abs(a) < 1e-8: # a close to 0 (avoid float comparison) + # use expression for limit a->0 from Srivastava et al. 2016 + return (z - x0) / (2 * z) + # expression from Bogacz et al. 2006 for nonzero drift + else: + ztilde, atilde, x0tilde = __standardize_bogacz(x0, a, z, s) + return 1 / (1 + exp(2 * ztilde * atilde)) - ( + (1 - exp(-2 * x0 * atilde)) / (exp(2 * ztilde * atilde) - exp(-2 * ztilde * atilde))) + + +def wfpt_dt_upper(x0, t0, a, z, s=1): + """ + Expected conditional first passage time, conditioned on crossing threshold + in the +drift direction (aka "upper" or "correct") + """ + + if abs(a) < 1e-8: # a close to 0 (avoid float comparison) + return (4 * x ** 2 - (z + x0) ** 2) / (3 * s ** 2) + kz, kx = __standardize_srivastava(x0, a, z, s) + return (s ** 2) / (a ** 2) * (2 * kz * coth(2 * kz) - (kx + kz) * coth(kx + kz)) + + +def wfpt_dt_lower(x0, t0, a, z, s=1): + """ + Expected conditional first passage time, conditioned on crossing threshold + in the -drift direction (aka "lower" or "error") + """ + return wfpt_dt_upper(-x0, t0, a, z, s) \ No newline at end of file diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index bfb1aa04043..b2912b40ca4 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -383,9 +383,11 @@ from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set, REPORT_OUTPUT_PREF from psyneulink.core.globals.preferences.preferenceset import PreferenceEntry, PreferenceLevel from psyneulink.core.globals.utilities import convert_to_np_array, is_numeric, is_same_function_spec, object_has_single_value, get_global_seed - +from psyneulink.core.scheduling.condition import AtTrialStart from psyneulink.core import llvm as pnlvm + + __all__ = [ 'ARRAY', 'DDM', 'DDMError', 'DECISION_VARIABLE', 'DECISION_VARIABLE_ARRAY', 'PROBABILITY_LOWER_THRESHOLD', 'PROBABILITY_UPPER_THRESHOLD', 'RESPONSE_TIME', @@ -447,7 +449,10 @@ class DDM(ProcessingMechanism): Implements a drift diffusion process (also known as the `Diffusion Decision Model `_, either by calculating an `analytic solution ` or carrying out `step-wise numerical integration `. - See `Mechanism ` for additional arguments and attributes. + See `Mechanism ` for additional arguments and attributes. The default behaviour + for the DDM is to reset its integration state on each new trial, this can be overridden by setting + the attribute to a different condition. For example, reset_stateful_function_when = pnl.Never() will cause + the DDM to never reset its state. Arguments @@ -852,6 +857,10 @@ def __init__(self, self._instantiate_plotting_functions() + # New (1/19/2021) default behavior of DDM mechanism is to reset stateful functions + # on each new trial. + self.reset_stateful_function_when = AtTrialStart() + def plot(self, stimulus=1.0, threshold=10.0): """ @@ -1086,6 +1095,7 @@ def _execute( return return_value def _gen_llvm_invoke_function(self, ctx, builder, function, params, state, variable, *, tags:frozenset): + mf_out, builder = super()._gen_llvm_invoke_function(ctx, builder, function, params, state, variable, tags=tags) mech_out_ty = ctx.convert_python_struct_to_llvm_ir(self.defaults.value) From e7856c0b84397a84a0057af891ef3c44107a7691 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 19 Jan 2021 21:22:22 -0500 Subject: [PATCH 016/285] Fixed up documentation for DDM default behaviour --- Scripts/Debug/ddm/ddm_plot_check.py | 2 -- .../mechanisms/processing/integrator/ddm.py | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py index a8d3887d85a..94639c3ebc6 100644 --- a/Scripts/Debug/ddm/ddm_plot_check.py +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -47,8 +47,6 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 comp = pnl.Composition() comp.add_node(decision) - comp.termination_processing = {pnl.TimeScale.TRIAL: pnl.WhenFinished(decision)} - context = pnl.Context() decision.function.parameters.rate.set(drift_rate, context) decision.function.parameters.noise.set(noise, context) diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index b2912b40ca4..d1035b33862 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -449,10 +449,15 @@ class DDM(ProcessingMechanism): Implements a drift diffusion process (also known as the `Diffusion Decision Model `_, either by calculating an `analytic solution ` or carrying out `step-wise numerical integration `. - See `Mechanism ` for additional arguments and attributes. The default behaviour - for the DDM is to reset its integration state on each new trial, this can be overridden by setting - the attribute to a different condition. For example, reset_stateful_function_when = pnl.Never() will cause - the DDM to never reset its state. + See `Mechanism ` for additional arguments and attributes. + + The default behaviour for the DDM is to reset its integration state on each new trial, this can be overridden by + setting the `reset_stateful_function_when ` attribute to a different condition. + In addition, unlike `TransferMechamism `, the DDM's + `execute_until_finished ` + attribute is set to :code:`True` by default. This will cause the DDM to execute multiple time steps per + call to its `execute ` method until the decision threshold is reached. This default behavior can + be changed by setting `execute_until_finished ` to :code:`False`. Arguments @@ -861,6 +866,9 @@ def __init__(self, # on each new trial. self.reset_stateful_function_when = AtTrialStart() + # New (1/19/2021) default behaviour of DDM mechanism is to execute until finished. That + # is, it should execute until it reaches its threshold. + self.execute_until_finished = True def plot(self, stimulus=1.0, threshold=10.0): """ From 6fa4dd5ee03c8642bec0ba6179563b04dab873f3 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 19 Jan 2021 21:58:08 -0500 Subject: [PATCH 017/285] Fixed typo in Component docs --- Scripts/Debug/ddm/ddm_plot_check.py | 13 ++++--------- psyneulink/core/components/component.py | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py index 94639c3ebc6..3a6e611164d 100644 --- a/Scripts/Debug/ddm/ddm_plot_check.py +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -36,11 +36,7 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 time_step_size=0.01, num_samples=1000000, use_pnl=True, rt_space=None): if use_pnl: - decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_value=0.8, - rate=-0.75, - threshold=0.8, - noise=0.1, - time_step_size=0.01), + decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_value=0.3), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], name='DDM') @@ -53,7 +49,6 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 decision.function.parameters.threshold.set(threshold, context) decision.function.parameters.time_step_size.set(time_step_size, context) decision.function.parameters.starting_value.set(np.array([starting_point]), context) - decision.function.parameters.time_step_size.set(time_step_size, context) decision.function.parameters.non_decision_time.set(non_decision_time, context) input = np.ones((1, 1)) @@ -112,7 +107,7 @@ def main(): ddm_params = dict(starting_point=0.0, drift_rate=0.1, noise=1.0, threshold=0.5, non_decision_time=0.0) - NUM_SAMPLES = 10000 + NUM_SAMPLES = 1000000 rt_space = np.linspace(0.0, 3.0, 30000) @@ -138,8 +133,8 @@ def main(): df = pd.concat([ anal_df, ddm_pdf_simulate(**ddm_params, time_step_size=0.01, use_pnl=True, num_samples=NUM_SAMPLES, rt_space=rt_space), - #ddm_pdf_simulate(**ddm_params, time_step_size=0.001, use_pnl=True, num_samples=NUM_SAMPLES, rt_space=rt_space), - #ddm_pdf_simulate(**ddm_params, time_step_size=0.0001, use_pnl=True, num_samples=NUM_SAMPLES, rt_space=rt_space), + ddm_pdf_simulate(**ddm_params, time_step_size=0.001, use_pnl=True, num_samples=NUM_SAMPLES, rt_space=rt_space), + ddm_pdf_simulate(**ddm_params, time_step_size=0.0001, use_pnl=True, num_samples=NUM_SAMPLES, rt_space=rt_space), ]) fig, axes = plt.subplots(1, 2, sharex=True, sharey=True) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 2b77700058d..8555d9ace30 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -384,7 +384,7 @@ .. note:: - `Mechanisms` ` are the only type of Component that reset when the `reset_stateful_function_when + `Mechanisms ` are the only type of Component that reset when the `reset_stateful_function_when ` `Condition` is satisfied. Other Component types do not reset, although `Composition` has a `reset ` method that can be used to reset all of its eligible Mechanisms (see `Composition_Reset`) From b787a68b752caae79ff7f305b11bded5cd667ff7 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 19 Jan 2021 22:39:02 -0500 Subject: [PATCH 018/285] DDM.max_executions_before_finished to sys.maxsize Default value of 1000 is arbitrary, should be unlimited in my opinion. When time_step_size gets below 0.001 then 1000 steps are often needed. Setting the sys.maxsize make its practically unlimited for now. This is a hack though, maybe there is a way to disable the limit but I can't find it in the docs. --- Scripts/Debug/ddm/ddm_plot_check.py | 6 ++++-- .../components/mechanisms/processing/integrator/ddm.py | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py index 3a6e611164d..12d19710d30 100644 --- a/Scripts/Debug/ddm/ddm_plot_check.py +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -36,7 +36,7 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 time_step_size=0.01, num_samples=1000000, use_pnl=True, rt_space=None): if use_pnl: - decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_value=0.3), + decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_value=0.8), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], name='DDM') @@ -49,6 +49,7 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 decision.function.parameters.threshold.set(threshold, context) decision.function.parameters.time_step_size.set(time_step_size, context) decision.function.parameters.starting_value.set(np.array([starting_point]), context) + decision.function.parameters.time_step_size.set(time_step_size, context) decision.function.parameters.non_decision_time.set(non_decision_time, context) input = np.ones((1, 1)) @@ -56,6 +57,7 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 comp.run(inputs={decision: input}, num_trials=num_samples * len(input), bin_execute=True, + reset_stateful_functions_when=pnl.AtTrialStart(), context=context) results = np.squeeze(np.array(comp.results)) @@ -107,7 +109,7 @@ def main(): ddm_params = dict(starting_point=0.0, drift_rate=0.1, noise=1.0, threshold=0.5, non_decision_time=0.0) - NUM_SAMPLES = 1000000 + NUM_SAMPLES = 100000 rt_space = np.linspace(0.0, 3.0, 30000) diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index d1035b33862..401922236b8 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -358,6 +358,7 @@ Class Reference --------------- """ +import sys import logging import types from collections.abc import Iterable @@ -870,6 +871,9 @@ def __init__(self, # is, it should execute until it reaches its threshold. self.execute_until_finished = True + # FIXME: Set maximum executions absurdly large to avoid early termination + self.max_executions_before_finished = sys.maxsize + def plot(self, stimulus=1.0, threshold=10.0): """ Generate a dynamic plot of the DDM integrating over time towards a threshold. From ad5db74e32b79a0dd2499a19c7fa6608a4f5e570 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 26 Jan 2021 13:25:48 -0500 Subject: [PATCH 019/285] fix: change default time_step_size for ddm The default was set to 1.0, this is kind of silly if the reaction times we are returning are meant to be real time. I set it to 0.01 --- Scripts/Debug/ddm/ddm_plot_check.py | 90 ++++++++++++------- .../core/components/functions/fitfunctions.py | 48 +++++----- .../statefulfunctions/integratorfunctions.py | 4 +- 3 files changed, 85 insertions(+), 57 deletions(-) diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py index 12d19710d30..223b5350ac8 100644 --- a/Scripts/Debug/ddm/ddm_plot_check.py +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -11,7 +11,7 @@ import wfpt -from psyneulink.core.components.functions.fitfunctions import simulation_likelihood +from psyneulink.core.components.functions.fitfunctions import simulation_likelihood, make_likelihood_function, MaxLikelihoodEstimator def ddm_pdf_analytical(drift_rate, threshold, noise, starting_point, non_decision_time, time_step_size=0.01): @@ -36,7 +36,7 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 time_step_size=0.01, num_samples=1000000, use_pnl=True, rt_space=None): if use_pnl: - decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_value=0.8), + decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_value=0.1234), output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], name='DDM') @@ -57,7 +57,6 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 comp.run(inputs={decision: input}, num_trials=num_samples * len(input), bin_execute=True, - reset_stateful_functions_when=pnl.AtTrialStart(), context=context) results = np.squeeze(np.array(comp.results)) @@ -96,8 +95,8 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 return df - -def main(): +def ddm_plot_check(): + ddm_params = dict(starting_point=0.1, drift_rate=0.3, noise=1.0, threshold=0.6, non_decision_time=0.8) # from numpy.random import rand # pd.DataFrame({ @@ -107,8 +106,6 @@ def main(): # 'noise': 1.0 # }) - ddm_params = dict(starting_point=0.0, drift_rate=0.1, noise=1.0, threshold=0.5, non_decision_time=0.0) - NUM_SAMPLES = 100000 rt_space = np.linspace(0.0, 3.0, 30000) @@ -148,27 +145,58 @@ def main(): plt.savefig(f"{'_'.join([f'{p}={v}' for p,v in ddm_params.items()])}.png") - # # Create a likelihood function from the composition itself, this is done - # # using probability density approximation via kernel density estimation. - # likelihood_func, param_map = make_likelihood_function(composition=comp, - # fit_params=[decision.function.parameters.rate, - # decision.function.parameters.starting_point], - # inputs=input, - # categorical_dims=np.array([True, False]), - # data_to_fit=data_to_fit, - # num_simulations=1000, - # fixed_params=dict( - # threshold=1.0, - # noise=0.1, - # time_step_size=0.01 - # ), - # combine_trials=True) - # - # params_to_fit = { - # 'rate': (0.0, 1.0), - # 'starting_point': (0.001, 1.0), - # } - - -if __name__ == "__main__": - main() + + +from psyneulink.core.components.functions.fitfunctions import simulation_likelihood, make_likelihood_function, \ + MaxLikelihoodEstimator + +ddm_params = dict(starting_value=0.1, rate=0.3, noise=1.0, threshold=0.6, non_decision_time=0.15) + +# Create a simple one mechanism composition containing a DDM in integrator mode. +decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(**ddm_params), + output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], + name='DDM') + +comp = pnl.Composition() +comp.add_node(decision) + +# Lets generate an "experimental" dataset to fit. This is a parameter recovery test +# The input will be 500 trials of the same constant stimulus drift rate of 1 +input = np.ones((500, 1)) +inputs_dict = {decision: input} + +# Run the composition to generate some data to fit +comp.run(inputs=inputs_dict, + num_trials=len(input), + bin_execute=True) + +# Store the results of this "experiment" as a numpy array. This should be a +# 2D array of shape (len(input), 2). The first column being a discrete variable +# specifying the upper or lower decision boundary and the second column is the +# reaction time. +data_to_fit = np.squeeze(np.array(comp.results)) + +# Create a likelihood function from the composition itself, this is done +# using probability density approximation via kernel density estimation. +likelihood_func, param_map = make_likelihood_function(composition=comp, + fit_params=[decision.function.parameters.rate, + decision.function.parameters.starting_value, + decision.function.parameters.non_decision_time], + inputs=inputs_dict, + categorical_dims=np.array([True, False]), + data_to_fit=data_to_fit, + num_simulations=1000, + fixed_params={ + decision.function.parameters.threshold: ddm_params['threshold'], + decision.function.parameters.noise: ddm_params['noise'], + decision.function.parameters.time_step_size: ddm_params['time_step_size'] + }, + combine_trials=True) + +print(likelihood_func(rate=0.3, starting_value=np.array([0.1]), non_decision_time=0.15)) + +params_to_fit = { + 'rate': (0.0, 1.0), + 'starting_point': (0.001, 1.0), +} + diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py index dd5f318f7f6..1aa92c1d880 100644 --- a/psyneulink/core/components/functions/fitfunctions.py +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -118,9 +118,9 @@ def simulation_likelihood(sim_data, # Get the right KDE for this trial, if all simulation trials have been combined # use that KDE for all trials of experimental data. if len(kdes) == 1: - kde, axes = kdes[0][exp_trial_cat] + kde, axes = kdes[0].get(exp_trial_cat, (None, None)) else: - kde, axes = kdes[trial][exp_trial_cat] + kde, axes = kdes[trial].get(exp_trial_cat, (None, None)) # Linear interpolation using the grid we computed the KDE # on. @@ -152,29 +152,29 @@ def make_likelihood_function(composition: 'psyneulink.core.composition.Compositi Parameters ---------- composition: A PsyNeuLink composition. This function returns a function that runs - many simulations of this composition to generate a kernel density estimate of the likelihood - of a dataset under different parameter settings. The output (composition.results) should match - the format in data_to_fit. + many simulations of this composition to generate a kernel density estimate of the likelihood + of a dataset under different parameter settings. The output (composition.results) should match + the format in data_to_fit. fit_params: A list of PsyNeuLink parameters to fit. Each on of these parameters will map to - an argument of the likelihood function that is returned. Values passed via these arguments - will be assigned to the composition before simulation. + an argument of the likelihood function that is returned. Values passed via these arguments + will be assigned to the composition before simulation. fixed_params: A dict of PsyNeuLink parameters and their corresponding fixed values. These - parameters will be applied to the composition before simulation but will not be exposed as - arguments to the likelihood function. + parameters will be applied to the composition before simulation but will not be exposed as + arguments to the likelihood function. inputs: A set of inputs to pass to the composition on each simulation of the likelihood. These - inputs are passed directly to the composition run method as is. + inputs are passed directly to the composition run method as is. categorical_dims: A 1D logical array, where each dimension corresponds to an output dimension - of the PsyNeuLink composition. If True, the dimension should be considered categorical, if False, - it should be treated as continuous. Categorical is suitable for outputs that will only take on - a handful of unique values, such as the decision value of a DDM. + of the PsyNeuLink composition. If True, the dimension should be considered categorical, if False, + it should be treated as continuous. Categorical is suitable for outputs that will only take on + a handful of unique values, such as the decision value of a DDM. data_to_fit: A 2D numpy array where the first dimension is the trial number and the columns are - in the same format as outputs of the PsyNeuLink composition. This data essentially describes at - what values the KDE of the likelihood should be evaluated. + in the same format as outputs of the PsyNeuLink composition. This data essentially describes at + what values the KDE of the likelihood should be evaluated. num_simulations: The number of simulations (per trial) to run to construct the KDE likelihood. combine_trials: Whether we can combine simulations across trials for one estimate of the likelihood. - This can dramatically increase the speed of the likelihood function by allowing a smaller number - of total simulations to run per trial. However, this cannot be done if the trial by trial state - of the composition is maintained. + This can dramatically increase the speed of the likelihood function by allowing a smaller number + of total simulations to run per trial. However, this cannot be done if the trial by trial state + of the composition is maintained. Returns ------- @@ -183,8 +183,9 @@ def make_likelihood_function(composition: 'psyneulink.core.composition.Compositi - A dict which maps elements of fit_params to their string function argument names. """ - # Get the number of trials in the input data - num_trials = len(next(iter(inputs))) + # We need to parse the inputs like composition does to get the number of trials + _, num_inputs_sets = composition._parse_run_inputs(inputs) + num_trials = num_inputs_sets # Check to see if any parameters (fittable or fixed) have the same name, # this will cause problems, if so, we need to create a unique numbering. @@ -215,14 +216,13 @@ def log_likelihood(**kwargs): param.set(value, context) # Also apply the fixed parameters - if fixed_params: - for param, value in fixed_params: + if fixed_params is not None: + for param, value in fixed_params.items(): param.set(value, context) # Run the composition for all simulations, this corresponds to looping over the input # num_simulations times. composition.run(inputs=inputs, - reset_stateful_functions_when=AtTrialStart(), num_trials=num_simulations * num_trials, bin_execute=True, context=context) @@ -230,7 +230,7 @@ def log_likelihood(**kwargs): results = np.squeeze(np.array(composition.results)) # Split results into (trials, simulations, data) - sim_data = np.array(np.vsplit(results, len(input))) + sim_data = np.array(np.vsplit(results, num_trials)) # Compute the likelihood given the data like = simulation_likelihood(sim_data=sim_data, exp_data=data_to_fit, diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index 3f69c5b8340..aebfa9d127f 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2127,7 +2127,7 @@ class DriftDiffusionIntegrator(IntegratorFunction): # ------------------------- offset= 0.0, \ non_decision_time=0.0, \ threshold=1.0 \ - time_step_size=1.0, \ + time_step_size=0.01, \ initializer=None, \ params=None, \ owner=None, \ @@ -2370,7 +2370,7 @@ class Parameters(IntegratorFunction.Parameters): initializer = Parameter(np.array([0]), aliases=['starting_value']) non_decision_time = Parameter(0.0, modulable=True) threshold = Parameter(100.0, modulable=True) - time_step_size = Parameter(1.0, modulable=True) + time_step_size = Parameter(0.01, modulable=True) previous_time = Parameter(None, initializer='non_decision_time', pnl_internal=True) seed = Parameter(None, read_only=True) random_state = Parameter(None, stateful=True, loggable=False) From 6074156789bb0469f4aff11b1e545c5dfcbaabd2 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 9 Feb 2021 15:31:42 -0500 Subject: [PATCH 020/285] Added a test of DDM made using taichi. Taichi example of the DDM. LCA is there as well but hasn't been tested well. Also added supported for simulation_likelihood taking 2D data arrays where the number of trials is 1. --- Scripts/Debug/ddm/ddm_plot_check.py | 52 ++-- Scripts/Debug/ddm/taichi_ddm.py | 37 +++ Scripts/Debug/ddm/taichi_test.py | 225 ++++++++++++++++++ Scripts/Debug/ddm/taichi_utils.py | 72 ++++++ .../core/components/functions/fitfunctions.py | 75 +++--- 5 files changed, 398 insertions(+), 63 deletions(-) create mode 100644 Scripts/Debug/ddm/taichi_ddm.py create mode 100644 Scripts/Debug/ddm/taichi_test.py create mode 100644 Scripts/Debug/ddm/taichi_utils.py diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py index 223b5350ac8..744da1af620 100644 --- a/Scripts/Debug/ddm/ddm_plot_check.py +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -95,6 +95,7 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 return df + def ddm_plot_check(): ddm_params = dict(starting_point=0.1, drift_rate=0.3, noise=1.0, threshold=0.6, non_decision_time=0.8) @@ -150,7 +151,8 @@ def ddm_plot_check(): from psyneulink.core.components.functions.fitfunctions import simulation_likelihood, make_likelihood_function, \ MaxLikelihoodEstimator -ddm_params = dict(starting_value=0.1, rate=0.3, noise=1.0, threshold=0.6, non_decision_time=0.15) +ddm_params = dict(starting_value=np.array([0.0]), rate=0.3, noise=1.0, + threshold=0.6, non_decision_time=0.15, time_step_size=0.01) # Create a simple one mechanism composition containing a DDM in integrator mode. decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(**ddm_params), @@ -178,25 +180,29 @@ def ddm_plot_check(): # Create a likelihood function from the composition itself, this is done # using probability density approximation via kernel density estimation. -likelihood_func, param_map = make_likelihood_function(composition=comp, - fit_params=[decision.function.parameters.rate, - decision.function.parameters.starting_value, - decision.function.parameters.non_decision_time], - inputs=inputs_dict, - categorical_dims=np.array([True, False]), - data_to_fit=data_to_fit, - num_simulations=1000, - fixed_params={ - decision.function.parameters.threshold: ddm_params['threshold'], - decision.function.parameters.noise: ddm_params['noise'], - decision.function.parameters.time_step_size: ddm_params['time_step_size'] - }, - combine_trials=True) - -print(likelihood_func(rate=0.3, starting_value=np.array([0.1]), non_decision_time=0.15)) - -params_to_fit = { - 'rate': (0.0, 1.0), - 'starting_point': (0.001, 1.0), -} - +likelihood_func, param_map = make_likelihood_function( + composition=comp, + fit_params=[decision.function.parameters.rate, + decision.function.parameters.threshold, + decision.function.parameters.non_decision_time], + fixed_params={ + decision.function.parameters.starting_value: ddm_params['starting_value'], + decision.function.parameters.noise: ddm_params['noise'], + decision.function.parameters.time_step_size: ddm_params['time_step_size'] + }, + inputs=inputs_dict, + categorical_dims=np.array([True, False]), + data_to_fit=data_to_fit, + num_simulations=1000, + combine_trials=True) + +mle = MaxLikelihoodEstimator(log_likelihood_function=likelihood_func, + fit_params_bounds={ + 'rate': (0.0, 1.0), + 'threshold': (0.0, 1.0), + 'non_decision_time': (0.0, 1.0), + }) + +fit_results = mle.fit() + +print(fit_results) diff --git a/Scripts/Debug/ddm/taichi_ddm.py b/Scripts/Debug/ddm/taichi_ddm.py new file mode 100644 index 00000000000..b012113c5a0 --- /dev/null +++ b/Scripts/Debug/ddm/taichi_ddm.py @@ -0,0 +1,37 @@ +import taichi as ti +import taichi_utils as tu + +num_simulations = 1000000 +rt = ti.field(ti.f32, num_simulations) +decision = ti.field(ti.i32, num_simulations) + +@ti.func +def ddm_time_step(prev_value, drift_rate, time_step_size): + return prev_value + (tu.rand_normal() + drift_rate * time_step_size) * ti.sqrt(time_step_size) + + +@ti.func +def simulate_ddm(starting_value, non_decision_time, drift_rate, threshold, time_step_size): + particle = starting_value + t = 0 + while abs(particle) < threshold: + particle = ddm_time_step(particle, drift_rate, time_step_size) + t = t + 1 + + rt = (non_decision_time + t * time_step_size) + decision = 1 + if particle < -threshold: + decision = 0 + + return rt, decision + + +@ti.kernel +def simulate_many_ddms(starting_value: ti.f32, + non_decision_time: ti.f32, + drift_rate: ti.f32, + threshold: ti.f32, + time_step_size: ti.f32): + for i in rt: + rt[i], decision[i] = simulate_ddm(starting_value, non_decision_time, + drift_rate, threshold, time_step_size) diff --git a/Scripts/Debug/ddm/taichi_test.py b/Scripts/Debug/ddm/taichi_test.py new file mode 100644 index 00000000000..4b0c8b1c2c1 --- /dev/null +++ b/Scripts/Debug/ddm/taichi_test.py @@ -0,0 +1,225 @@ +#%% +import numpy as np +import pandas as pd +import math +import taichi as ti +import taichi_glsl as ts +import taichi_utils as tu + +from ddm import Model +from ddm.models import DriftConstant, NoiseConstant, BoundConstant, OverlayNonDecision, ICPoint +from ddm.functions import display_model +from psyneulink.core.components.functions.fitfunctions import simulation_likelihood, make_likelihood_function, MaxLikelihoodEstimator + + +ti.init(arch=ti.gpu) + +#%% +num_simulations = 1000000 +rt = ti.field(ti.f32, num_simulations) +decision = ti.field(ti.i32, num_simulations) +max_time = 3.0 + + +def ddm_pdf_analytical(drift_rate, threshold, starting_value, non_decision_time, noise=1.0, time_step_size=0.001): + + model = Model(name='Simple model', + drift=DriftConstant(drift=drift_rate), + noise=NoiseConstant(noise=noise), + bound=BoundConstant(B=threshold), + IC=ICPoint(x0=starting_value), + overlay=OverlayNonDecision(nondectime=non_decision_time), + dx=.001, dt=time_step_size, T_dur=3) + s = model.solve() + + return model.t_domain(), s.pdf_corr(), s.pdf_err() + + +@ti.func +def ddm_time_step(prev_value, drift_rate, time_step_size): + return prev_value + (tu.rand_normal() + drift_rate * time_step_size) * ti.sqrt(time_step_size) + + +@ti.func +def simulate_ddm(starting_value, non_decision_time, drift_rate, threshold, time_step_size): + particle = starting_value + t = 0 + while abs(particle) < threshold: + particle = ddm_time_step(particle, drift_rate, time_step_size) + t = t + 1 + + rt = (non_decision_time + t * time_step_size) + decision = 1 + if particle < -threshold: + decision = 0 + + return rt, decision + + +@ti.kernel +def simulate_many_ddms(starting_value: ti.f32, + non_decision_time: ti.f32, + drift_rate: ti.f32, + threshold: ti.f32, + time_step_size: ti.f32): + for i in rt: + rt[i], decision[i] = simulate_ddm(starting_value, non_decision_time, + drift_rate, threshold, time_step_size) + + +@ti.func +def lca_time_step(prev_value, prev_value_f, stimulus, gamma, leak, time_step_size): + drift = time_step_size * (stimulus - leak * prev_value + gamma @ prev_value_f) + return prev_value + (drift + tu.rand_normal2()) * ti.sqrt(time_step_size) + + +@ti.func +def simulate_lca(stimulus, competition, self_excitation, + leak, gain, starting_value, + threshold, non_decision_time, time_step_size): + gamma = ti.Matrix([[competition, self_excitation], [self_excitation, competition]], dt=ti.f32) + + pre_activation = ti.Vector([starting_value, starting_value], dt=ti.f32) + particle = tu.relu(pre_activation) + t = 0 + while particle.max() < threshold and t * time_step_size < max_time: + pre_activation = lca_time_step(pre_activation, particle, stimulus, gamma, leak, time_step_size) + particle = tu.relu(pre_activation) + t = t + 1 + + rt = (non_decision_time + t * time_step_size) + + # If the simulation exceeds the max time, we terminated early, set RT to negative to signal this + # is a failed simulation + if rt >= max_time: + rt = -1 + + # Figure out which threshold was crossed. + decision = 0 + if particle[0] >= threshold: + decision = 0 + + if particle[1] >= threshold: + decision = 1 + + # If multiple dimensions crossed the threshold at the same time then this is a failure case + # as well. With infinite precision this won't happen. + if particle[0] >= threshold and particle[1] >= threshold: + rt = -1 + + return rt, decision + + +stimulus = ti.Vector.field(2, dtype=float, shape=()) +stimulus[None] = [0.1, 0.2] + +@ti.kernel +def simulate_many_lcas(competition: ti.f32, + self_excitation: ti.f32, + leak: ti.f32, + gain: ti.f32, + starting_value: ti.f32, + threshold: ti.f32, + non_decision_time: ti.f32, + time_step_size: ti.f32): + stimulus_vec = stimulus[None] + for i in rt: + rt[i], decision[i] = simulate_lca(stimulus_vec, competition, self_excitation, + leak, gain, starting_value, + threshold, non_decision_time, time_step_size) + + +ddm_params = dict(starting_value=0.0, non_decision_time=0.14, + drift_rate=0.1, threshold=0.6, time_step_size=0.001) +lca_params = dict(competition=0.1, self_excitation=0.1, + leak=0.1, gain=1.0, starting_value=0.0, threshold=0.08, + non_decistion_time=0.3, time_step_size=0.0001) + + +#simulate_many_ddms(*list(ddm_params.values())) +simulate_many_lcas(*list(lca_params.values())) +rts = rt.to_numpy() +choices = decision.to_numpy() + +# import time +# t0 = time.time() +# for i in range(50): +# simulate_many_lcas(*list(lca_params.values())) +# rts = rt.to_numpy() +# choices = decision.to_numpy() +# print(f"Elapsed: {((time.time()-t0)/50.0)*1000} milliseconds") + +ti.sync() +rts = rt.to_numpy() +choices = decision.to_numpy() +valid = rts > 0.0 +rts = rts[valid] +choices = choices[valid] + +# import time +# t0 = time.time() +# +# NUM_TIMES = 50 +# for i in range(NUM_TIMES): +# simulate_many_ddms(*list(ddm_params.values())) +# rts_np = rts.to_numpy() +# +# print(f"Elapsed: { 1000*((time.time() - t0) / NUM_TIMES)} milliseconds") + + +#%% +import time +import matplotlib.pyplot as plt +import seaborn as sns +import boost_histogram as bh +import functools +import operator +from psyneulink.core.components.functions.fitfunctions import simulation_likelihood, make_likelihood_function, MaxLikelihoodEstimator + +rt_space = np.linspace(0, 3.0, num=3000) + +t0 = time.time() +# pdf0 = simulation_likelihood(np.column_stack((choices, rts)), categorical_dims=np.array([True, False]), +# exp_data=np.c_[np.zeros(len(rt_space)), rt_space]) +# pdf1 = simulation_likelihood(np.column_stack((choices, rts)), categorical_dims=np.array([True, False]), +# exp_data=np.c_[np.ones(len(rt_space)), rt_space]) + +hist = bh.Histogram(bh.axis.Integer(0, 2), bh.axis.Regular(3000, 0.0, 3.0)) +hist.fill(choices, rts) +areas = functools.reduce(operator.mul, hist.axes.widths) +density = hist.view() / hist.sum() / areas +pdf0 = density[0, :] +pdf1 = density[1, :] + +print(f"Elapsed: { 1000*(time.time() - t0)} milliseconds") + +df = pd.DataFrame(index=rt_space) +df[f'Correct KDE (dt={ddm_params["time_step_size"]})'] = pdf1 +df[f'Error KDE (dt={ddm_params["time_step_size"]})'] = pdf0 + + +# # Get the analytical +# t_domain, pdf_corr, pdf_err = ddm_pdf_analytical(**ddm_params) +# +# # Interpolate to common rt space +# from scipy.interpolate import interpn +# +# anal_df = pd.DataFrame(index=rt_space) +# anal_df[f"Correct Analytical"] = interpn((t_domain,), pdf_corr, rt_space, +# method='linear', bounds_error=False, fill_value=1e-10) +# anal_df[f"Error Analytical"] = interpn((t_domain,), pdf_err, rt_space, +# method='linear', bounds_error=False, fill_value=1e-10) +# +# df = pd.concat([anal_df, df]) + +fig, axes = plt.subplots(1, 2, sharex=True, sharey=True) +sns.lineplot(data=df.filter(regex='Correct'), ax=axes[0]) +sns.lineplot(data=df.filter(regex='Error'), ax=axes[1]) +plt.show() + +#%% + + + + + diff --git a/Scripts/Debug/ddm/taichi_utils.py b/Scripts/Debug/ddm/taichi_utils.py new file mode 100644 index 00000000000..e7a1099a0f7 --- /dev/null +++ b/Scripts/Debug/ddm/taichi_utils.py @@ -0,0 +1,72 @@ +import math +import taichi as ti +import taichi_glsl as ts + + +@ti.func +def rand_normal(): + """ + Generate a normally distributed random number with mean=0 and variance=1 + + Returns + ------- + A scalar randome number + """ + u0 = ti.random(ti.f32) + u1 = ti.random(ti.f32) + r = ti.sqrt(-2*ti.log(u0)) + theta = 2 * math.pi * u1 + r0 = r * ti.sin(theta) + #r1 = r * ti.cos(theta) + return r0 + +@ti.func +def rand_normal2(): + """ + Generate a normally distributed random number with mean=0 and variance=1 + + Returns + ------- + A scalar randome number + """ + u0 = ti.random(ti.f32) + u1 = ti.random(ti.f32) + r = ti.sqrt(-2*ti.log(u0)) + theta = 2 * math.pi * u1 + r0 = r * ti.sin(theta) + r1 = r * ti.cos(theta) + return ti.Vector([r0, r1]) + + + +@ti.func +def rand_normal_vec(): + v = ti.Vector([0.0, 0.0]) + # for i in ti.static(range(0, len(v), 2)): + # u0 = ti.random(ti.f32) + # u1 = ti.random(ti.f32) + # r = ti.sqrt(-2*ti.log(u0)) + # theta = 2 * math.pi * u1 + # r0 = r * ti.sin(theta) + # v[i] = r0 + # v[i+1] = r * ti.cos(theta) + + return v + + +@ti.pyfunc +def relu(v: ti.template()): + return (ti.abs(v) + v) / 2.0 + +@ti.pyfunc +def logistic(v: ti.template(), gain): + return 1.0 / (1.0 + ti.exp(-gain * v)) + + +@ti.func +def argmax(v: ti.template()): + maxval = v.max() + for i in ti.static(range(2)): + if v[i] == maxval: + return i + diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py index 1aa92c1d880..04b0d37222c 100644 --- a/psyneulink/core/components/functions/fitfunctions.py +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -48,16 +48,18 @@ def simulation_likelihood(sim_data, Parameters ---------- - sim_data: This must be a 3D numpy array where the first dimension is the trial, the - second dimension is the simulation number, and the final dimension is data points. + sim_data: Data collected over many simulations. This must be either a 2D or 3D numpy array. + If 2D, the first dimension is the simulation number and the second dimension is data points. That is, + each row is a simulation. If 3D, the first dimension is the trial, the second dimension is the + simulation number, and the final dimension is data points. exp_data: This must be a numpy array with identical format as the simulation data, with the exception - that there is no simulation dimension. + that there is no simulation dimension. categorical_dims: a list of indices that indicate categorical dimensions of a data point. combine_trials: Combine data across all trials into a single likelihood estimate, this assumes - that the parameters of the simulations are identical across trials. + that the parameters of the simulations are identical across trials. Returns ------- @@ -66,6 +68,10 @@ def simulation_likelihood(sim_data, """ + # Add a singleton dimension for trials if needed. + if sim_data.ndim == 2: + sim_data = sim_data[None, :, :] + if combine_trials and sim_data.shape[0] > 1: sim_data = np.vstack(sim_data)[None, :, :] @@ -252,59 +258,49 @@ class MaxLikelihoodEstimator: def __init__(self, log_likelihood_function: typing.Callable, - fit_params_bounds: typing.Dict[str, typing.Tuple], - fixed_params: typing.Optional[typing.Dict[Parameter, typing.Any]]): + fit_params_bounds: typing.Dict[str, typing.Tuple]): self.log_likelihood_function = log_likelihood_function self.fit_params_bounds = fit_params_bounds - if fixed_params is not None: - self.fixed_params = fixed_params - else: - self.fixed_params = {} + def _print_param_vec(self, p, end="\n"): + print(', '.join(f'{name}={value:.5f}' for name, value in p.items()), end=end) def fit(self): bounds = list(self.fit_params_bounds.values()) - # Check if any of are fixed params are in parameters to fit, this is a mistake - for fixed_p, val in self.fixed_params.items(): - if fixed_p in self.fit_params_bounds: - raise ValueError(f"Fixed parameter ({fixed_p}) is also present in the parameters to fit.") - - def print_param_vec(p, end="\n"): - print(', '.join(f'{name}={value:.5f}' for name, value in p.items()), end=end) - # Create a wrapper function for the objective. def neg_log_like(x): params = dict(zip(self.fit_params_bounds.keys(), x)) - # print_param_vec(params, end="") - - p = -self.log_likelihood_function(**self.fixed_params, **params) - # print(f" neg_log_like={p:.5f}") + t0 = time.time() + p = -self.log_likelihood_function(**params) + elapsed = time.time() - t0 + self._print_param_vec(params, end="") + print(f", Neg-Log-Likelihood: {p}, CallTime={elapsed}") return p - t0 = time.time() + # If the user has rich installed, make a nice progress bar + try: + from rich.progress import Progress - def print_callback(x, convergence): - global t0 - t1 = time.time() - params = dict(zip(self.fit_params_bounds.keys(), x)) - print_param_vec(params, end="") - print(f", convergence={convergence:.5f}, iter_time={t1 - t0} secs") - t0 = t1 + with Progress() as progress: + opt_task = progress.add_task("Maximum likelihood optimization ...", total=100, start=False) + + def progress_callback(x, convergence): + convergence_pct = 100.0 * convergence + progress.update(opt_task, completed=convergence_pct) - t0 = time.time() + r = differential_evolution(neg_log_like, bounds, callback=progress_callback, maxiter=500) - # with Progress() as progress: - # opt_task = progress.add_task("Maximum likelihood optimization ...", total=100, start=False) - # - # def progress_callback(x, convergence): - # convergence = 100.0 * convergence - # progress.update(opt_task, completed=convergence) + # Otherwise, just print crap to the console + except ModuleNotFoundError: - r = differential_evolution(neg_log_like, bounds, callback=print_callback, maxiter=500, workers=6) + def progress_callback(x, convergence): + params = dict(zip(self.fit_params_bounds.keys(), x)) + self._print_param_vec(params, end="") + print(f", convergence={convergence:.5f}") - print(f"Search Time: {(time.time() - t0) / 60.0} minutes") + r = differential_evolution(neg_log_like, bounds, callback=progress_callback, maxiter=500) # Bind the fitted parameters to their names fitted_params = dict(zip(list(self.fit_params_bounds.keys()), r.x)) @@ -312,7 +308,6 @@ def print_callback(x, convergence): # Save all the results output_dict = { - 'fixed_params': self.fixed_params, 'fitted_params': fitted_params, 'likelihood': r.fun, } From 3a1d590a45db51dec600106b5226ffd54e6f15b9 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 19 Jan 2021 21:57:08 -0500 Subject: [PATCH 021/285] DriftDiffusionIntegrator: avoid always producing 3d value --- .../statefulfunctions/integratorfunctions.py | 32 ++++++++++++------- .../mechanisms/processing/integrator/ddm.py | 11 +++---- tests/composition/test_composition.py | 4 +-- tests/mechanisms/test_ddm_mechanism.py | 30 ++++++++--------- tests/mechanisms/test_integrator_mechanism.py | 6 ++-- tests/mechanisms/test_mechanisms.py | 6 ++-- tests/scheduling/test_scheduler.py | 8 ++--- 7 files changed, 52 insertions(+), 45 deletions(-) diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index aff06fe8a65..0c7ed623ea4 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -44,7 +44,7 @@ MULTIPLICATIVE_PARAM, NOISE, OFFSET, OPERATION, ORNSTEIN_UHLENBECK_INTEGRATOR_FUNCTION, OUTPUT_PORTS, PRODUCT, \ RATE, REST, SIMPLE_INTEGRATOR_FUNCTION, SUM, TIME_STEP_SIZE, THRESHOLD, VARIABLE from psyneulink.core.globals.parameters import Parameter -from psyneulink.core.globals.utilities import parameter_spec, all_within_range, iscompatible, get_global_seed +from psyneulink.core.globals.utilities import parameter_spec, all_within_range, iscompatible, get_global_seed, convert_all_elements_to_np_array from psyneulink.core.globals.context import Context, ContextFlags, handle_external_context from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set @@ -372,7 +372,11 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, # Get rid of 2d array. # When part of a Mechanism, the input and output are 2d arrays. arg_in = pnlvm.helpers.unwrap_2d_array(builder, arg_in) - arg_out = pnlvm.helpers.unwrap_2d_array(builder, arg_out) + + # output may be 2d with multiple items (e.g. DriftDiffusionIntegrator, + # FitzHughNagumoIntegrator) + if arg_out.type.pointee.count == 1: + arg_out = pnlvm.helpers.unwrap_2d_array(builder, arg_out) with pnlvm.helpers.array_ptr_loop(builder, arg_in, "integrate") as args: self._gen_llvm_integrate(*args, ctx, arg_in, arg_out, params, state) @@ -2377,6 +2381,12 @@ class Parameters(IntegratorFunction.Parameters): read_only=True ) + def _parse_initializer(self, initializer): + if initializer.ndim > 1: + return np.atleast_1d(initializer.squeeze()) + else: + return initializer + @tc.typecheck def __init__(self, default_variable=None, @@ -2419,6 +2429,9 @@ def _validate_noise(self, noise): "Invalid noise parameter for {}: {}. DriftDiffusionIntegrator requires noise parameter to be a float or float array." " Noise parameter is used to construct the standard DDM noise distribution".format(self.name, type(noise))) + def _initialize_previous_value(self, initializer, context=None): + return super()._initialize_previous_value(self.parameters._parse_initializer(initializer), context) + def _function(self, variable=None, context=None, @@ -2451,7 +2464,8 @@ def _function(self, 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.parameters.previous_value._get(context)) + variable = self.parameters._parse_initializer(variable) + previous_value = self.parameters.previous_value._get(context) random_draw = np.array([random_state.normal() for _ in list(variable)]) value = previous_value + rate * variable * time_step_size \ @@ -2466,16 +2480,11 @@ def _function(self, if not self.is_initializing: previous_value = adjusted_value previous_time = previous_time + time_step_size - if not np.isscalar(variable): - previous_time = np.broadcast_to( - previous_time, - variable.shape - ).copy() self.parameters.previous_time._set(previous_time, context) self.parameters.previous_value._set(previous_value, context) - return previous_value, previous_time + return convert_all_elements_to_np_array([previous_value, previous_time]) def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): # Get parameter pointers @@ -2502,8 +2511,7 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): # value = previous_value + rate * variable * time_step_size \ # + np.sqrt(time_step_size * noise) * random_state.normal() - prev_val_ptr = builder.gep(prev_ptr, [ctx.int32_ty(0), - ctx.int32_ty(0), index]) + prev_val_ptr = builder.gep(prev_ptr, [ctx.int32_ty(0), index]) prev_val = builder.load(prev_val_ptr) val = builder.load(builder.gep(vi, [ctx.int32_ty(0), index])) if isinstance(val.type, pnlvm.ir.ArrayType): @@ -2526,7 +2534,7 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): val = pnlvm.helpers.fclamp(builder, val, neg_threshold, threshold) # Store value result - data_vo_ptr = builder.gep(vo, [ctx.int32_ty(0), ctx.int32_ty(0), + data_vo_ptr = builder.gep(vo, [ctx.int32_ty(0), ctx.int32_ty(0), index]) builder.store(val, data_vo_ptr) builder.store(val, prev_val_ptr) diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index 22c74a72532..bbd297a8643 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -382,7 +382,7 @@ from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set, REPORT_OUTPUT_PREF from psyneulink.core.globals.preferences.preferenceset import PreferenceEntry, PreferenceLevel -from psyneulink.core.globals.utilities import convert_to_np_array, is_numeric, is_same_function_spec, object_has_single_value, get_global_seed +from psyneulink.core.globals.utilities import convert_all_elements_to_np_array, is_numeric, is_same_function_spec, object_has_single_value, get_global_seed from psyneulink.core import llvm as pnlvm @@ -1047,7 +1047,7 @@ def _execute( if self.initialization_status != ContextFlags.INITIALIZING: logger.info('{0} {1} is at {2}'.format(type(self).__name__, self.name, result)) - return convert_to_np_array([result[0], [result[1]]]) + return convert_all_elements_to_np_array([result[0], result[1]]) # EXECUTE ANALYTIC SOLUTION (TRIAL TIME SCALE) ----------------------------------------------------------- else: @@ -1101,8 +1101,7 @@ def _gen_llvm_invoke_function(self, ctx, builder, function, params, state, varia builder.store(builder.load(builder.gep(mf_out, [ctx.int32_ty(0), ctx.int32_ty(1)])), builder.gep(mech_out, [ctx.int32_ty(0), - ctx.int32_ty(1), - ctx.int32_ty(0)])) + ctx.int32_ty(1)])) elif isinstance(self.function, DriftDiffusionAnalytical): for res_idx, idx in enumerate((self.RESPONSE_TIME_INDEX, self.PROBABILITY_LOWER_THRESHOLD_INDEX, @@ -1161,7 +1160,7 @@ def reset(self, *args, force=False, context=None, **kwargs): # (1) reset function, (2) update mechanism value, (3) update output ports if isinstance(self.function, IntegratorFunction): new_values = self.function.reset(*args, **kwargs, context=context) - self.parameters.value._set(convert_to_np_array(new_values), context) + self.parameters.value._set(convert_all_elements_to_np_array(new_values), context) self._update_output_ports(context=context) @handle_external_context() @@ -1207,7 +1206,7 @@ def _gen_llvm_is_finished_cond(self, ctx, builder, params, state): return pnlvm.ir.IntType(1)(1) # Extract scalar value from ptr - prev_val_ptr = builder.gep(prev_val_ptr, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + prev_val_ptr = builder.gep(prev_val_ptr, [ctx.int32_ty(0), ctx.int32_ty(0)]) prev_val = builder.load(prev_val_ptr) # Take abs of previous val diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 7608c3e79bd..015e02a17bc 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -6606,9 +6606,9 @@ def test_save_state_before_simulations(self): assert np.allclose(np.asfarray(run_2_values), np.asfarray(run_3_values)) assert np.allclose(np.asfarray(run_1_values), - [np.array([[0.36]]), np.array([[0.056]]), np.array([[0.056]])]) + [np.array([0.36]), np.array([0.056]), np.array([0.056])]) assert np.allclose(np.asfarray(run_2_values), - [np.array([[0.5904]]), np.array([[0.16384]]), np.array([[0.16384]])]) + [np.array([0.5904]), np.array([0.16384]), np.array([0.16384])]) class TestNodeRoles: diff --git a/tests/mechanisms/test_ddm_mechanism.py b/tests/mechanisms/test_ddm_mechanism.py index f8fda48d54d..9888cfdaa41 100644 --- a/tests/mechanisms/test_ddm_mechanism.py +++ b/tests/mechanisms/test_ddm_mechanism.py @@ -29,8 +29,8 @@ def test_valid(self): # 0.0 + 1.0 * 1.0 * 1.0 + 0.0 D.execute(1.0) assert np.allclose(np.asfarray(D.value), [[1.0], [1.0]]) - assert np.allclose(D.output_ports[0].value[0][0], 1.0) - assert np.allclose(D.output_ports[1].value[0][0], 1.0) + assert np.allclose(D.output_ports[0].value[0], 1.0) + assert np.allclose(D.output_ports[1].value[0], 1.0) # reset function D.function.reset(2.0, 0.1) @@ -38,8 +38,8 @@ def test_valid(self): assert np.allclose(D.function.previous_value, 2.0) assert np.allclose(D.function.previous_time, 0.1) assert np.allclose(np.asfarray(D.value), [[1.0], [1.0]]) - assert np.allclose(D.output_ports[0].value[0][0], 1.0) - assert np.allclose(D.output_ports[1].value[0][0], 1.0) + assert np.allclose(D.output_ports[0].value[0], 1.0) + assert np.allclose(D.output_ports[1].value[0], 1.0) # reset function without value spec D.function.reset() @@ -47,8 +47,8 @@ def test_valid(self): assert np.allclose(D.function.previous_value, 0.0) assert np.allclose(D.function.previous_time, 0.0) assert np.allclose(np.asfarray(D.value), [[1.0], [1.0]]) - assert np.allclose(D.output_ports[0].value[0][0], 1.0) - assert np.allclose(D.output_ports[1].value[0][0], 1.0) + assert np.allclose(D.output_ports[0].value[0], 1.0) + assert np.allclose(D.output_ports[1].value[0], 1.0) # reset mechanism D.reset(2.0, 0.1) @@ -61,9 +61,9 @@ def test_valid(self): D.execute(1.0) # 2.0 + 1.0 = 3.0 ; 0.1 + 1.0 = 1.1 - assert np.allclose(np.asfarray(D.value), [[[3.0]], [[1.1]]]) - assert np.allclose(D.output_ports[0].value[0][0], 3.0) - assert np.allclose(D.output_ports[1].value[0][0], 1.1) + assert np.allclose(np.asfarray(D.value), [[3.0], [1.1]]) + assert np.allclose(D.output_ports[0].value[0], 3.0) + assert np.allclose(D.output_ports[1].value[0], 1.1) # reset mechanism without value spec D.reset() @@ -132,8 +132,8 @@ def test_threshold_stops_accumulation(self, mode, variable, expected, benchmark) time_points = [] for i in range(5): output = ex([variable]) - decision_variables.append(output[0][0][0]) - time_points.append(output[1][0][0]) + decision_variables.append(output[0][0]) + time_points.append(output[1][0]) # decision variable accumulation stops assert np.allclose(decision_variables, expected) @@ -320,7 +320,7 @@ def test_DDM_noise(mode, benchmark, noise, expected): ex = pnlvm.execution.MechExecution(T).cuda_execute val = ex([10]) - assert np.allclose(val[0][0][0], expected) + assert np.allclose(val[0][0], expected) if benchmark.enabled: benchmark(ex, [10]) @@ -456,7 +456,7 @@ def test_DDM_rate(benchmark, rate, expected, mode): ex = pnlvm.execution.MechExecution(T).execute elif mode == 'PTX': ex = pnlvm.execution.MechExecution(T).cuda_execute - val = float(ex(stim)[0][0][0]) + val = float(ex(stim)[0][0]) assert val == expected if benchmark.enabled: benchmark(ex, stim) @@ -628,12 +628,12 @@ def test_DDM_time(): np.testing.assert_allclose(time_0, 0.5, atol=1e-08) time_1 = D.execute(10)[1][0] # t_1 = 0.5 + 0.2 = 0.7 - np.testing.assert_allclose(time_1[0], 0.7, atol=1e-08) + np.testing.assert_allclose(time_1, 0.7, atol=1e-08) for i in range(10): # t_11 = 0.7 + 10*0.2 = 2.7 D.execute(10) time_12 = D.execute(10)[1][0] # t_12 = 2.7 + 0.2 = 2.9 - np.testing.assert_allclose(time_12[0], 2.9, atol=1e-08) + np.testing.assert_allclose(time_12, 2.9, atol=1e-08) def test_WhenFinished_DDM_Analytical(): diff --git a/tests/mechanisms/test_integrator_mechanism.py b/tests/mechanisms/test_integrator_mechanism.py index 4face0ae73c..fd7088e9a04 100644 --- a/tests/mechanisms/test_integrator_mechanism.py +++ b/tests/mechanisms/test_integrator_mechanism.py @@ -562,7 +562,7 @@ def test_drift_diffusion_integrator(self): # P = Process(pathway=[I]) # 10 + 10*0.5 + 0 + 10 = 25 val = I.execute(1) - assert np.allclose([[[25.]], [[0.5]]], val) + assert np.allclose([[25.], [0.5]], val) @pytest.mark.mechanism @pytest.mark.integrator_mechanism @@ -798,7 +798,7 @@ def test_integrator_type_diffusion_rate_float(self): ) # P = Process(pathway=[I]) val = I.execute(10.0) - assert np.allclose([[[50.0]], [[1.0]]], val) + assert np.allclose([[50.0], [1.0]], val) # rate = list, integration_type = simple @@ -1178,7 +1178,7 @@ def test_integrator_drift_diffusion_noise_val(self): ) val = I.execute(10.0) - assert np.allclose(val, [[[4.29013944]], [[ 1. ]]]) + assert np.allclose(val, [[4.29013944], [ 1. ]]) # COMMENTED OUT UNTIL OU INTEGRATOR IS VALIDATED @pytest.mark.mechanism diff --git a/tests/mechanisms/test_mechanisms.py b/tests/mechanisms/test_mechanisms.py index 4bd4b8bc9d0..b4f974df3f4 100644 --- a/tests/mechanisms/test_mechanisms.py +++ b/tests/mechanisms/test_mechanisms.py @@ -159,7 +159,7 @@ def test_reset_state_integrator_mechanism(self): # Execute A twice # [0] saves decision variable only (not time) - original_output = [A.execute(1.0)[0], A.execute(1.0)[0]] + original_output = [A.execute(1.0), A.execute(1.0)] # SAVING STATE - - - - - - - - - - - - - - - - - - - - - - - - - reset_stateful_functions_to = {} @@ -169,13 +169,13 @@ def test_reset_state_integrator_mechanism(self): print(reset_stateful_functions_to) # Execute A twice AFTER saving the state so that it continues accumulating. # We expect the next two outputs to repeat once we reset the state b/c we will return it to the current state - output_after_saving_state = [A.execute(1.0)[0], A.execute(1.0)[0]] + output_after_saving_state = [A.execute(1.0), A.execute(1.0)] # RESETTING STATE - - - - - - - - - - - - - - - - - - - - - - - - A.reset(**reset_stateful_functions_to) # We expect these results to match the results from immediately after saving the state - output_after_reinitialization = [A.execute(1.0)[0], A.execute(1.0)[0]] + output_after_reinitialization = [A.execute(1.0), A.execute(1.0)] assert np.allclose(output_after_saving_state, output_after_reinitialization) assert np.allclose(original_output, [np.array([[1.0]]), np.array([[2.0]])]) diff --git a/tests/scheduling/test_scheduler.py b/tests/scheduling/test_scheduler.py index d43e6fefd42..e4426170946 100644 --- a/tests/scheduling/test_scheduler.py +++ b/tests/scheduling/test_scheduler.py @@ -187,10 +187,10 @@ def change_termination_processing(): # Trial 1: # input = 2.0, termination condition = AllHaveRun # 1 pass (value = 2.0) - expected_results = [[np.array([[10.]]), np.array([[10.]])], - [np.array([[2.]]), np.array([[1.]])], - [np.array([[10.]]), np.array([[10.]])], - [np.array([[2.]]), np.array([[1.]])]] + expected_results = [[np.array([10.]), np.array([10.])], + [np.array([2.]), np.array([1.])], + [np.array([10.]), np.array([10.])], + [np.array([2.]), np.array([1.])]] assert np.allclose(expected_results, np.asfarray(C.results)) From 86b5977133d13d938afaa94b4f246d080fcdcad7 Mon Sep 17 00:00:00 2001 From: David Turner Date: Sun, 21 Feb 2021 19:55:56 -0500 Subject: [PATCH 022/285] Working example of DDM fitting. --- Scripts/Debug/ddm/ddm_plot_check.py | 26 +-- .../core/components/functions/fitfunctions.py | 180 ++++++++++++++---- 2 files changed, 157 insertions(+), 49 deletions(-) diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py index 744da1af620..ebafdce1671 100644 --- a/Scripts/Debug/ddm/ddm_plot_check.py +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -151,7 +151,7 @@ def ddm_plot_check(): from psyneulink.core.components.functions.fitfunctions import simulation_likelihood, make_likelihood_function, \ MaxLikelihoodEstimator -ddm_params = dict(starting_value=np.array([0.0]), rate=0.3, noise=1.0, +ddm_params = dict(starting_value=0.0, rate=0.3, noise=1.0, threshold=0.6, non_decision_time=0.15, time_step_size=0.01) # Create a simple one mechanism composition containing a DDM in integrator mode. @@ -178,31 +178,31 @@ def ddm_plot_check(): # reaction time. data_to_fit = np.squeeze(np.array(comp.results)) + + # Create a likelihood function from the composition itself, this is done # using probability density approximation via kernel density estimation. -likelihood_func, param_map = make_likelihood_function( +likelihood, param_map = make_likelihood_function( composition=comp, fit_params=[decision.function.parameters.rate, - decision.function.parameters.threshold, + decision.function.parameters.starting_value, decision.function.parameters.non_decision_time], - fixed_params={ - decision.function.parameters.starting_value: ddm_params['starting_value'], - decision.function.parameters.noise: ddm_params['noise'], - decision.function.parameters.time_step_size: ddm_params['time_step_size'] - }, inputs=inputs_dict, categorical_dims=np.array([True, False]), data_to_fit=data_to_fit, - num_simulations=1000, + num_simulations=100, combine_trials=True) -mle = MaxLikelihoodEstimator(log_likelihood_function=likelihood_func, +params_to_recover = {k: ddm_params[k] for k in param_map.values()} +print(f"Parameters to recover: {params_to_recover}") +print(f"Data Neg-Log-Likelihood: {-likelihood(**params_to_recover)}") + +mle = MaxLikelihoodEstimator(log_likelihood_function=likelihood, fit_params_bounds={ 'rate': (0.0, 1.0), - 'threshold': (0.0, 1.0), + 'starting_value': (0.0, 1.0), 'non_decision_time': (0.0, 1.0), }) -fit_results = mle.fit() +fit_results = mle.fit(display_iter=True, save_iterations=True) -print(fit_results) diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py index 04b0d37222c..a9cbe8f1a16 100644 --- a/psyneulink/core/components/functions/fitfunctions.py +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -1,6 +1,13 @@ import typing import time import numpy as np +import collections +import pandas as pd +from rich.progress import Progress, BarColumn, TimeRemainingColumn + +import warnings +import logging +logger = logging.getLogger(__name__) from psyneulink.core.globals.context import Context from psyneulink.core.globals.parameters import Parameter @@ -14,6 +21,31 @@ from scipy.optimize import differential_evolution +def get_param_str(params): + """ + A simple function to turn a dict into a string with commas separating key=value pairs. + + Parameters + ---------- + params: The parameters to print. + + Returns + ------- + The string version of the parameter dict + + """ + return ", ".join(f'{name}={value:.5f}' for name, value in params.items()) + + +class BadLikelihoodWarning(UserWarning): + """ + A custom warning that is used to signal when the likelihood could not be evaluated for some reason. + This is usually caused when parameter values cause degenerate simulation results (no variance in values). + It can also be caused when experimental data is not matching any of the simulation results. + """ + pass + + def simulation_likelihood(sim_data, exp_data=None, categorical_dims=None, @@ -25,7 +57,7 @@ def simulation_likelihood(sim_data, If no experimental data is provided just return the KDE evaluated at default points provided by the fastkde library. - Reference: + Some related work: Steven Miletić, Brandon M. Turner, Birte U. Forstmann, Leendert van Maanen, Parameter recovery for the Leaky Competing Accumulator model, @@ -37,6 +69,8 @@ def simulation_likelihood(sim_data, https://doi.org/10.1016/j.jmp.2016.12.001. (http://www.sciencedirect.com/science/article/pii/S0022249616301663) + This function uses the wonderful fastKDE package: + O’Brien, T. A., Kashinath, K., Cavanaugh, N. R., Collins, W. D. & O’Brien, J. P. A fast and objective multidimensional kernel density estimation method: fastKDE. Comput. Stat. Data Anal. 101, 148–160 (2016). @@ -96,6 +130,16 @@ def simulation_likelihood(sim_data, dens_u[category] = (None, None) continue + # If any dimension of the data has a 0 range (all are same value) then + # this will cause problems doing the KDE, skip. + data_range = np.max(dsub) - np.min(dsub) if dsub.ndim == 1 else np.amax(dsub, 1) - np.amin(dsub, 1) + if np.any(data_range == 0): + dens_u[category] = (None, None) + warnings.warn(BadLikelihoodWarning( + f"Could not perform kernel density estimate. Range of simulation data was 0 for at least " + f"one dimension. Range={data_range}")) + continue + # Do KDE fKDE = fastKDE.fastKDE(dsub, doSaveMarginals=False) pdf = fKDE.pdf @@ -112,6 +156,10 @@ def simulation_likelihood(sim_data, # If we are passed experimental data, evaluate the KDE at the experimental data points if exp_data is not None: + # For numerical reasons, make zero probability a really small value. This is because we are taking logs + # of the probabilities at the end. + ZERO_PROB = 1e-10 + kdes_eval = np.zeros((len(exp_data),)) for trial in range(len(exp_data)): @@ -132,9 +180,21 @@ def simulation_likelihood(sim_data, # on. if kde is not None: kdes_eval[trial] = interpn(axes, kde, exp_data[trial, ~categorical_dims], - method='linear', bounds_error=False, fill_value=1e-10) + method='linear', bounds_error=False, fill_value=ZERO_PROB) else: - kdes_eval[trial] = 1e-10 + kdes_eval[trial] = ZERO_PROB + + + # Check to see if any of the trials have non-zero likelihood, if not, something is probably wrong + # and we should warn the user. + if np.alltrue(kdes_eval == ZERO_PROB): + warnings.warn(BadLikelihoodWarning( + "Evaluating likelihood generated by simulation data resulted in zero values for all trials " + "of experimental data. This means the model is not generating data similar to your " + "experimental data. If you have categorical dimensions, make sure values match exactly to " + "output values of the composition. Also make sure parameter ranges you are searching over " + "are reasonable for your data.")) + return kdes_eval @@ -218,6 +278,10 @@ def log_likelihood(**kwargs): except KeyError: raise ValueError(f"No argument {param_name_map[param]} passed to likelihood function for Parameter: \n{param}") + # FIXME: DDM requires that starting_value is an array in compiled mode for some reason. Ugly hack! + if param.name == "starting_value" and type(value) != np.ndarray: + value = np.array([value]) + # Apply the parameter value under a fresh context param.set(value, context) @@ -226,6 +290,9 @@ def log_likelihood(**kwargs): for param, value in fixed_params.items(): param.set(value, context) + # FIXME: Multiple calls to run retain results, we need to clear them. Is this OK? + composition.results.clear() + # Run the composition for all simulations, this corresponds to looping over the input # num_simulations times. composition.run(inputs=inputs, @@ -262,55 +329,96 @@ def __init__(self, self.log_likelihood_function = log_likelihood_function self.fit_params_bounds = fit_params_bounds - def _print_param_vec(self, p, end="\n"): - print(', '.join(f'{name}={value:.5f}' for name, value in p.items()), end=end) + # Setup a named tuple to store records for each iteration if the user requests it + self.IterRecord = collections.namedtuple('IterRecord', + f"{' '.join(self.fit_params_bounds.keys())} neg_log_likelihood likelihood_eval_time") - def fit(self): + def fit(self, + display_iter: bool = False, + save_iterations: bool = False): bounds = list(self.fit_params_bounds.values()) - # Create a wrapper function for the objective. - def neg_log_like(x): - params = dict(zip(self.fit_params_bounds.keys(), x)) - t0 = time.time() - p = -self.log_likelihood_function(**params) - elapsed = time.time() - t0 - self._print_param_vec(params, end="") - print(f", Neg-Log-Likelihood: {p}, CallTime={elapsed}") - return p - # If the user has rich installed, make a nice progress bar - try: - from rich.progress import Progress - - with Progress() as progress: - opt_task = progress.add_task("Maximum likelihood optimization ...", total=100, start=False) + from rich.progress import Progress + + iterations = [] + + with Progress( + "[progress.description]{task.description}", + BarColumn(), + "Convergence: [progress.percentage]{task.percentage:>3.0f}%", + TimeRemainingColumn(), + ) as progress: + opt_task = progress.add_task("Maximum likelihood optimization ...", total=100, start=False) + + warns_with_params = [] + with warnings.catch_warnings(record=True) as warns: + + # Create a wrapper function for the objective. + def neg_log_like(x): + params = dict(zip(self.fit_params_bounds.keys(), x)) + t0 = time.time() + p = -self.log_likelihood_function(**params) + elapsed = time.time() - t0 + + # Keep a log of warnings and the parameters that caused them + if len(warns) > 0 and warns[-1].category == BadLikelihoodWarning: + warns_with_params.append((warns[-1], params)) + + # Are we displaying each iteration + if display_iter: + + # If we got a warning generating the likelihood, report it + if len(warns) > 0 and warns[-1].category == BadLikelihoodWarning: + progress.console.print(f"Warning: ", style="bold red") + progress.console.print(f"{warns[-1].message}", style="bold red") + progress.console.print(f"{get_param_str(params)}, Neg-Log-Likelihood: {p}, " + f"Likelihood-Eval-Time: {elapsed} (seconds)", style="bold red") + # Clear the warnings + warns.clear() + else: + progress.console.print(f"{get_param_str(params)}, Neg-Log-Likelihood: {p}, " + f"Likelihood-Eval-Time: {elapsed} (seconds)") + + # Are we saving each iteration + if save_iterations: + iterations.append(self.IterRecord(**params, neg_log_likelihood=p, likelihood_eval_time=elapsed)) + + return p def progress_callback(x, convergence): + progress.start_task(opt_task) + params = dict(zip(self.fit_params_bounds.keys(), x)) convergence_pct = 100.0 * convergence - progress.update(opt_task, completed=convergence_pct) + progress.console.print(f"[green]Current Best Parameters: {get_param_str(params)}, Neg-Log-Likelihood: {neg_log_like(x)}") + + # If we encounter any BadLikelihoodWarnings. Summarize them for the user + if len(warns_with_params) > 0: + progress.console.print("Warning: degenerate likelihood for the following parameter values ", style='bold red') + for w in warns_with_params: + progress.console.print(f"\t{get_param_str(w[1])}", style='bold red') + progress.console.print("If these warnings are intermittent, check to see if search " + "space is appropriately bounded. If they are constant, make sure " + "experimental data and output of your composition are similar.", + style='bold red') - r = differential_evolution(neg_log_like, bounds, callback=progress_callback, maxiter=500) - - # Otherwise, just print crap to the console - except ModuleNotFoundError: - - def progress_callback(x, convergence): - params = dict(zip(self.fit_params_bounds.keys(), x)) - self._print_param_vec(params, end="") - print(f", convergence={convergence:.5f}") + progress.update(opt_task, completed=convergence_pct) - r = differential_evolution(neg_log_like, bounds, callback=progress_callback, maxiter=500) + r = differential_evolution(neg_log_like, bounds, callback=progress_callback, maxiter=500, polish=False) - # Bind the fitted parameters to their names - fitted_params = dict(zip(list(self.fit_params_bounds.keys()), r.x)) - print(f"Fitted Params: \n\t{fitted_params}\nLikelihood: {r.fun}") + # Bind the fitted parameters to their names + fitted_params = dict(zip(list(self.fit_params_bounds.keys()), r.x)) # Save all the results output_dict = { 'fitted_params': fitted_params, - 'likelihood': r.fun, + 'neg-log-likelihood': r.fun, } + # Return a record for each iteration if we were supposed to. + if save_iterations: + output_dict['iterations'] = pd.DataFrame.from_records(iterations, columns=self.IterRecord._fields) + return output_dict From 13dd753413ca01be9a2bb5406b673a3c920b0cac Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 23 Feb 2021 10:42:32 -0500 Subject: [PATCH 023/285] Pandas support addded --- Scripts/Debug/ddm/ddm_fit.py | 69 +++++++++++++++++++ Scripts/Debug/ddm/ddm_plot_check.py | 61 +--------------- .../core/components/functions/fitfunctions.py | 60 ++++++++++------ psyneulink/core/compositions/composition.py | 15 ++++ requirements.txt | 2 + 5 files changed, 127 insertions(+), 80 deletions(-) create mode 100644 Scripts/Debug/ddm/ddm_fit.py diff --git a/Scripts/Debug/ddm/ddm_fit.py b/Scripts/Debug/ddm/ddm_fit.py new file mode 100644 index 00000000000..2fd73a5e418 --- /dev/null +++ b/Scripts/Debug/ddm/ddm_fit.py @@ -0,0 +1,69 @@ +#%% +import numpy as np +import pandas as pd +import psyneulink as pnl + +import matplotlib.pyplot as plt +plt.rcParams["figure.figsize"] = (20,10) + +import matplotlib.pyplot as plt +import seaborn as sns +import pandas as pd + +from psyneulink.core.components.functions.fitfunctions import make_likelihood_function, \ + MaxLikelihoodEstimator + +ddm_params = dict(starting_value=0.0, rate=0.3, noise=1.0, + threshold=0.6, non_decision_time=0.15, time_step_size=0.01) + +# Create a simple one mechanism composition containing a DDM in integrator mode. +decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(**ddm_params), + output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], + name='DDM') + +comp = pnl.Composition() +comp.add_node(decision) + +# Lets generate an "experimental" dataset to fit. This is a parameter recovery test +# The input will be 500 trials of the same constant stimulus drift rate of 1 +input = np.ones((500, 1)) +inputs_dict = {decision: input} + +# Run the composition to generate some data to fit +comp.run(inputs=inputs_dict, + num_trials=len(input), + bin_execute=True) + +# Store the results of this "experiment" as a numpy array. This should be a +# 2D array of shape (len(input), 2). The first column being a discrete variable +# specifying the upper or lower decision boundary and the second column is the +# reaction time. We will put the data into a pandas DataFrame, this makes its +# easier to specify which columns in the data are categorical or not. +data_to_fit = pd.DataFrame(np.squeeze(np.array(comp.results)), + columns=['decision', 'rt']) +data_to_fit['decision'] = pd.Categorical(data_to_fit['decision']) + + +# Create a likelihood function from the composition itself, this is done +# using probability density approximation via kernel density estimation. +likelihood, param_map = comp.make_likelihood_function( + fit_params=[decision.function.parameters.rate, + decision.function.parameters.starting_value, + decision.function.parameters.non_decision_time], + inputs=inputs_dict, + data_to_fit=data_to_fit, + num_sims_per_trial=100, + combine_trials=True) + +params_to_recover = {k: ddm_params[k] for k in param_map.values()} +print(f"Parameters to recover: {params_to_recover}") +print(f"Data Neg-Log-Likelihood: {-likelihood(**params_to_recover)}") + +mle = MaxLikelihoodEstimator(log_likelihood_function=likelihood, + fit_params_bounds={ + 'rate': (0.0, 1.0), + 'starting_value': (0.0, 1.0), + 'non_decision_time': (0.0, 1.0), + }) + +fit_results = mle.fit(display_iter=False, save_iterations=True) \ No newline at end of file diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py index ebafdce1671..5aea1660f66 100644 --- a/Scripts/Debug/ddm/ddm_plot_check.py +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -11,7 +11,7 @@ import wfpt -from psyneulink.core.components.functions.fitfunctions import simulation_likelihood, make_likelihood_function, MaxLikelihoodEstimator +from psyneulink.core.components.functions.fitfunctions import simulation_likelihood def ddm_pdf_analytical(drift_rate, threshold, noise, starting_point, non_decision_time, time_step_size=0.01): @@ -147,62 +147,5 @@ def ddm_plot_check(): plt.savefig(f"{'_'.join([f'{p}={v}' for p,v in ddm_params.items()])}.png") - -from psyneulink.core.components.functions.fitfunctions import simulation_likelihood, make_likelihood_function, \ - MaxLikelihoodEstimator - -ddm_params = dict(starting_value=0.0, rate=0.3, noise=1.0, - threshold=0.6, non_decision_time=0.15, time_step_size=0.01) - -# Create a simple one mechanism composition containing a DDM in integrator mode. -decision = pnl.DDM(function=pnl.DriftDiffusionIntegrator(**ddm_params), - output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], - name='DDM') - -comp = pnl.Composition() -comp.add_node(decision) - -# Lets generate an "experimental" dataset to fit. This is a parameter recovery test -# The input will be 500 trials of the same constant stimulus drift rate of 1 -input = np.ones((500, 1)) -inputs_dict = {decision: input} - -# Run the composition to generate some data to fit -comp.run(inputs=inputs_dict, - num_trials=len(input), - bin_execute=True) - -# Store the results of this "experiment" as a numpy array. This should be a -# 2D array of shape (len(input), 2). The first column being a discrete variable -# specifying the upper or lower decision boundary and the second column is the -# reaction time. -data_to_fit = np.squeeze(np.array(comp.results)) - - - -# Create a likelihood function from the composition itself, this is done -# using probability density approximation via kernel density estimation. -likelihood, param_map = make_likelihood_function( - composition=comp, - fit_params=[decision.function.parameters.rate, - decision.function.parameters.starting_value, - decision.function.parameters.non_decision_time], - inputs=inputs_dict, - categorical_dims=np.array([True, False]), - data_to_fit=data_to_fit, - num_simulations=100, - combine_trials=True) - -params_to_recover = {k: ddm_params[k] for k in param_map.values()} -print(f"Parameters to recover: {params_to_recover}") -print(f"Data Neg-Log-Likelihood: {-likelihood(**params_to_recover)}") - -mle = MaxLikelihoodEstimator(log_likelihood_function=likelihood, - fit_params_bounds={ - 'rate': (0.0, 1.0), - 'starting_value': (0.0, 1.0), - 'non_decision_time': (0.0, 1.0), - }) - -fit_results = mle.fit(display_iter=True, save_iterations=True) +ddm_plot_check() diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py index a9cbe8f1a16..e9e5ea5dc37 100644 --- a/psyneulink/core/components/functions/fitfunctions.py +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -53,9 +53,8 @@ def simulation_likelihood(sim_data, """ Compute the likelihood of a simulation dataset (or the parameters that generated it) conditional on a set of experimental data. This function essentially just computes the kernel density estimate (KDE) - of the simulation data at the experimental data points. - If no experimental data is provided just return the KDE evaluated at default points provided by the fastkde - library. + of the simulation data at the experimental data points. If no experimental data is provided just return + the KDE evaluated at default points provided by the fastkde library. Some related work: @@ -90,7 +89,8 @@ def simulation_likelihood(sim_data, exp_data: This must be a numpy array with identical format as the simulation data, with the exception that there is no simulation dimension. - categorical_dims: a list of indices that indicate categorical dimensions of a data point. + categorical_dims: a list of indices that indicate categorical dimensions of a data point. Length must be + the same length as last dimension of sim_data and exp_data. combine_trials: Combine data across all trials into a single likelihood estimate, this assumes that the parameters of the simulations are identical across trials. @@ -109,6 +109,9 @@ def simulation_likelihood(sim_data, if combine_trials and sim_data.shape[0] > 1: sim_data = np.vstack(sim_data)[None, :, :] + if type(categorical_dims) != np.ndarray: + categorical_dims = np.array(categorical_dims) + con_sim_data = sim_data[:, :, ~categorical_dims] cat_sim_data = sim_data[:, :, categorical_dims] @@ -195,7 +198,6 @@ def simulation_likelihood(sim_data, "output values of the composition. Also make sure parameter ranges you are searching over " "are reasonable for your data.")) - return kdes_eval else: @@ -205,9 +207,9 @@ def simulation_likelihood(sim_data, def make_likelihood_function(composition: 'psyneulink.core.composition.Composition', fit_params: typing.List[Parameter], inputs: typing.Union[np.ndarray, typing.List], - categorical_dims: np.ndarray, - data_to_fit: np.ndarray, - num_simulations: int = 1000, + data_to_fit: typing.Union[np.ndarray, pd.DataFrame], + categorical_dims: typing.Union[np.ndarray, None] = None, + num_sims_per_trial: int = 1000, fixed_params: typing.Optional[typing.Dict[Parameter, typing.Any]] = None, combine_trials=True): """ @@ -220,7 +222,7 @@ def make_likelihood_function(composition: 'psyneulink.core.composition.Compositi composition: A PsyNeuLink composition. This function returns a function that runs many simulations of this composition to generate a kernel density estimate of the likelihood of a dataset under different parameter settings. The output (composition.results) should match - the format in data_to_fit. + the columns of data_to_fit exactly. fit_params: A list of PsyNeuLink parameters to fit. Each on of these parameters will map to an argument of the likelihood function that is returned. Values passed via these arguments will be assigned to the composition before simulation. @@ -229,24 +231,25 @@ def make_likelihood_function(composition: 'psyneulink.core.composition.Compositi arguments to the likelihood function. inputs: A set of inputs to pass to the composition on each simulation of the likelihood. These inputs are passed directly to the composition run method as is. - categorical_dims: A 1D logical array, where each dimension corresponds to an output dimension - of the PsyNeuLink composition. If True, the dimension should be considered categorical, if False, - it should be treated as continuous. Categorical is suitable for outputs that will only take on - a handful of unique values, such as the decision value of a DDM. - data_to_fit: A 2D numpy array where the first dimension is the trial number and the columns are + categorical_dims: If data_to_fit is a pandas DataFrame, this parameter is ignored and any Categorical column + is considered categorical. If data_to_fit is a ndarray, categorical_dims should be a 1D logical array, where + each element corresponds to a column of data_to_fit. If the element is True, the dimension should be considered + categorical, if False, it should be treated as continuous. Categorical is suitable for outputs that will only + take on a handful of unique values, such as the decision value of a DDM or LCA. + data_to_fit: Either 2D numpy array or Pandas DataFrame, where the rows are trials and the columns are in the same format as outputs of the PsyNeuLink composition. This data essentially describes at what values the KDE of the likelihood should be evaluated. - num_simulations: The number of simulations (per trial) to run to construct the KDE likelihood. + num_sims_per_trial: The number of simulations per trial to run to construct the KDE likelihood. combine_trials: Whether we can combine simulations across trials for one estimate of the likelihood. - This can dramatically increase the speed of the likelihood function by allowing a smaller number - of total simulations to run per trial. However, this cannot be done if the trial by trial state - of the composition is maintained. + This can dramatically increase the speed of fitting by allowing a smaller number + of total simulations to run per trial. However, this cannot be done if the likelihood will change across + trials. Returns ------- A tuple containing: - the likelihood function - - A dict which maps elements of fit_params to their string function argument names. + - A dict which maps elements of fit_params to their keyword argument names in the likelihood function. """ # We need to parse the inputs like composition does to get the number of trials @@ -262,6 +265,20 @@ def make_likelihood_function(composition: 'psyneulink.core.composition.Compositi all_param_names = [name if count == 1 else f"{name}_{count}" for name, count in zip(all_param_names, dupe_counts)] param_name_map = dict(zip(fit_params, all_param_names)) + if type(data_to_fit) == np.ndarray: + if data_to_fit.ndim != 2: + raise ValueError("data_to_fit must be a 2D") + + # Assume all dimensions are continuous if this wasn't specified by the user and their data is a numpy array + if categorical_dims is None: + categorical_dims = [False for i in range(data_to_fit.shape[1])] + + elif type(data_to_fit) == pd.DataFrame: + categorical_dims = [data_to_fit[c].dtype.name == "category" for c in data_to_fit.columns] + + else: + raise ValueError("data_to_fit must be a 2D numpy array or a Pandas DataFrame") + def log_likelihood(**kwargs): context = Context() @@ -296,7 +313,7 @@ def log_likelihood(**kwargs): # Run the composition for all simulations, this corresponds to looping over the input # num_simulations times. composition.run(inputs=inputs, - num_trials=num_simulations * num_trials, + num_trials=num_sims_per_trial * num_trials, bin_execute=True, context=context) @@ -306,7 +323,8 @@ def log_likelihood(**kwargs): sim_data = np.array(np.vsplit(results, num_trials)) # Compute the likelihood given the data - like = simulation_likelihood(sim_data=sim_data, exp_data=data_to_fit, + like = simulation_likelihood(sim_data=sim_data, + exp_data=data_to_fit.to_numpy().astype(float), categorical_dims=categorical_dims, combine_trials=combine_trials) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 5816ae9d660..251602b81c8 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2415,6 +2415,7 @@ def input_function(env, result): from psyneulink.library.components.mechanisms.modulatory.learning.autoassociativelearningmechanism import \ AutoAssociativeLearningMechanism from psyneulink.library.components.projections.pathway.autoassociativeprojection import AutoAssociativeProjection +from psyneulink.core.components.functions.fitfunctions import make_likelihood_function __all__ = [ 'Composition', 'CompositionError', 'CompositionRegistry', 'EdgeType', 'get_compositions', 'NodeRole' @@ -10157,6 +10158,20 @@ def _set_up_animation(self, context): def _animate_execution(self, active_items, context): self._show_graph._animate_execution(active_items, context) + def make_likelihood_function(self, *args, **kwargs): + """ + This method invokes :func:`~psyneulink.core.components.functions.fitfunctions.make_likelihood_function` + on the composition. + """ + return make_likelihood_function(composition=self, *args, **kwargs) + + def fit(self, *args, **kwargs): + """ + Fit parameters of this composition to an experimental dataset. + """ + raise NotImplemented("Need to implement this!") + + def get_compositions(): """Return list of Compositions in caller's namespace.""" diff --git a/requirements.txt b/requirements.txt index 18ba0229f80..8f8deeb2e4e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,5 @@ torch<1.8.0; sys_platform != 'win32' and platform_machine == 'x86_64' and platfo typecheck-decorator<=1.2 leabra-psyneulink<=0.3.2 rich<=9.11.0 +pandas<=1.0.5 + From 0456962e8845e72ce4f0b447299e68ae70a49944 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 23 Feb 2021 14:17:47 -0500 Subject: [PATCH 024/285] Fixed a lot of old DDM tests. Some important changes to DDM api caused test failures. Most of the tests were fixed by simply passing parameters to the DDM so that it behaves like it used to by default. Namely these are the following: DDM(execute_until_finished=False, reset_stateful_functions_when=Never()) and DriftDiffusionIntegrator(time_step_size=1.0) This is because the following changes have been made to DDM: - DDM now executes until finished by default - DDM resets after each trial. - Default time_step_size is 0.01 instead of 1.0 - Old DDM was computing std of noise wrong by applying a sqrt(noise). Now noise is the actual std of the noise. --- Scripts/Debug/ddm/ddm_fit.py | 14 ++---- psyneulink/core/compositions/composition.py | 7 --- .../mechanisms/processing/integrator/ddm.py | 4 +- requirements.txt | 1 + tests/mechanisms/test_ddm_mechanism.py | 46 ++++++++++++++----- 5 files changed, 42 insertions(+), 30 deletions(-) diff --git a/Scripts/Debug/ddm/ddm_fit.py b/Scripts/Debug/ddm/ddm_fit.py index 2fd73a5e418..409e54420c6 100644 --- a/Scripts/Debug/ddm/ddm_fit.py +++ b/Scripts/Debug/ddm/ddm_fit.py @@ -3,13 +3,6 @@ import pandas as pd import psyneulink as pnl -import matplotlib.pyplot as plt -plt.rcParams["figure.figsize"] = (20,10) - -import matplotlib.pyplot as plt -import seaborn as sns -import pandas as pd - from psyneulink.core.components.functions.fitfunctions import make_likelihood_function, \ MaxLikelihoodEstimator @@ -24,6 +17,8 @@ comp = pnl.Composition() comp.add_node(decision) +#%% + # Lets generate an "experimental" dataset to fit. This is a parameter recovery test # The input will be 500 trials of the same constant stimulus drift rate of 1 input = np.ones((500, 1)) @@ -43,6 +38,7 @@ columns=['decision', 'rt']) data_to_fit['decision'] = pd.Categorical(data_to_fit['decision']) +#%% # Create a likelihood function from the composition itself, this is done # using probability density approximation via kernel density estimation. @@ -62,8 +58,8 @@ mle = MaxLikelihoodEstimator(log_likelihood_function=likelihood, fit_params_bounds={ 'rate': (0.0, 1.0), - 'starting_value': (0.0, 1.0), + 'starting_value': (0.0, 0.9), 'non_decision_time': (0.0, 1.0), }) -fit_results = mle.fit(display_iter=False, save_iterations=True) \ No newline at end of file +fit_results = mle.fit(display_iter=True, save_iterations=True) \ No newline at end of file diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 251602b81c8..354859685c9 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -10165,13 +10165,6 @@ def make_likelihood_function(self, *args, **kwargs): """ return make_likelihood_function(composition=self, *args, **kwargs) - def fit(self, *args, **kwargs): - """ - Fit parameters of this composition to an experimental dataset. - """ - raise NotImplemented("Need to implement this!") - - def get_compositions(): """Return list of Compositions in caller's namespace.""" diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index 09fe4d3f005..444520246d2 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -865,11 +865,11 @@ def __init__(self, # New (1/19/2021) default behavior of DDM mechanism is to reset stateful functions # on each new trial. - self.reset_stateful_function_when = AtTrialStart() + self.reset_stateful_function_when = kwargs.get('reset_stateful_function_when', AtTrialStart()) # New (1/19/2021) default behaviour of DDM mechanism is to execute until finished. That # is, it should execute until it reaches its threshold. - self.execute_until_finished = True + self.execute_until_finished = kwargs.get('execute_until_finished', True) # FIXME: Set maximum executions absurdly large to avoid early termination self.max_executions_before_finished = sys.maxsize diff --git a/requirements.txt b/requirements.txt index 8f8deeb2e4e..8c94d72a682 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,5 @@ typecheck-decorator<=1.2 leabra-psyneulink<=0.3.2 rich<=9.11.0 pandas<=1.0.5 +fastkde diff --git a/tests/mechanisms/test_ddm_mechanism.py b/tests/mechanisms/test_ddm_mechanism.py index eb26f3e476e..3735b5ed097 100644 --- a/tests/mechanisms/test_ddm_mechanism.py +++ b/tests/mechanisms/test_ddm_mechanism.py @@ -22,7 +22,8 @@ class TestReset: def test_valid(self): D = DDM( name='DDM', - function=DriftDiffusionIntegrator(seed=0), + function=DriftDiffusionIntegrator(seed=0, time_step_size=1.0), + execute_until_finished=False, ) # returns previous_value + rate * variable * time_step_size + noise @@ -87,7 +88,7 @@ def test_valid(self): class TestThreshold: def test_threshold_param(self): D = DDM(name='DDM', - function=DriftDiffusionIntegrator(threshold=10.0)) + function=DriftDiffusionIntegrator(threshold=10.0, time_step_size=1.0)) assert D.function.threshold.base == 10.0 @@ -96,7 +97,9 @@ def test_threshold_param(self): def test_threshold_sets_is_finished(self): D = DDM(name='DDM', - function=DriftDiffusionIntegrator(threshold=5.0)) + function=DriftDiffusionIntegrator(threshold=5.0, time_step_size=1.0), + execute_until_finished=False, + reset_stateful_function_when=Never()) D.execute(2.0) # 2.0 < 5.0 assert not D.is_finished() @@ -120,7 +123,8 @@ def test_threshold_sets_is_finished(self): ]) def test_threshold_stops_accumulation(self, mode, variable, expected, benchmark): D = DDM(name='DDM', - function=DriftDiffusionIntegrator(threshold=5.0)) + function=DriftDiffusionIntegrator(threshold=5.0, time_step_size=1.0), + execute_until_finished=False) if mode == 'Python': ex = D.execute elif mode == 'LLVM': @@ -165,7 +169,9 @@ def test_threshold_stops_accumulation(self, mode, variable, expected, benchmark) def test_is_finished_stops_composition(self): D = DDM(name='DDM', - function=DriftDiffusionIntegrator(threshold=10.0)) + function=DriftDiffusionIntegrator(threshold=10.0, time_step_size=1.0), + execute_until_finished=False, + reset_stateful_function_when=Never()) C = Composition(pathways=[D], reset_stateful_function_when=Never()) C.run(inputs={D: 2.0}, termination_processing={TimeScale.TRIAL: WhenFinished(D)}) @@ -295,8 +301,8 @@ def test_DDM_Integrator_Bogacz(benchmark, mode): @pytest.mark.benchmark(group="DDM") @pytest.mark.parametrize("noise, expected", [ (0., 10), - (0.5, 8.194383551861414), - (2., 6.388767103722829), + (np.sqrt(0.5), 8.194383551861414), + (np.sqrt(2.0), 6.388767103722829), ], ids=["0", "0.5", "2.0"]) @pytest.mark.parametrize("mode", [ 'Python', @@ -310,7 +316,8 @@ def test_DDM_noise(mode, benchmark, noise, expected): noise=noise, rate=1.0, time_step_size=1.0 - ) + ), + execute_until_finished=False ) if mode == 'Python': ex = T.execute @@ -367,6 +374,7 @@ def test_DDM_input(stim): rate=1.0, time_step_size=1.0 ), + execute_until_finished=False, ) val = float(T.execute(stim)[0]) assert val == 10 @@ -392,6 +400,7 @@ def test_DDM_input_list_len_2(): rate=1.0, time_step_size=1.0 ), + execute_until_finished=False, ) float(T.execute(stim)[0]) assert "single numeric item" in str(error_text.value) @@ -417,6 +426,7 @@ def test_DDM_input_fn(): rate=1.0, time_step_size=1.0 ), + execute_until_finished=False, ) float(T.execute(stim)) assert "not supported for the input types" in str(error_text.value) @@ -449,6 +459,7 @@ def test_DDM_rate(benchmark, rate, expected, mode): rate=rate, time_step_size=1.0 ), + execute_until_finished=False, ) if mode == 'Python': ex = T.execute @@ -486,6 +497,7 @@ def test_DDM_rate_fn(): rate=NormalDist().function, time_step_size=1.0 ), + execute_until_finished=False, ) float(T.execute(stim)[0]) assert "incompatible value" in str(error_text.value) @@ -511,6 +523,7 @@ def test_DDM_size_int_check_var(): rate=-5.0, time_step_size=1.0 ), + execute_until_finished=False, ) assert len(T.defaults.variable) == 1 and T.defaults.variable[0][0] == 0 @@ -528,6 +541,7 @@ def test_DDM_size_int_inputs(): rate=-5.0, time_step_size=1.0 ), + execute_until_finished=False, ) val = T.execute([.4]) decision_variable = val[0][0] @@ -554,6 +568,7 @@ def test_DDM_mech_size_zero(): rate=-5.0, time_step_size=1.0 ), + execute_until_finished=False, ) assert "is not a positive number" in str(error_text.value) @@ -572,6 +587,7 @@ def test_DDM_mech_size_negative_one(): rate=-5.0, time_step_size=1.0 ), + execute_until_finished=False, ) assert "is not a positive number" in str(error_text.value) @@ -590,6 +606,7 @@ def test_DDM_size_too_large(): rate=-5.0, time_step_size=1.0 ), + execute_until_finished=False, ) assert "single numeric item" in str(error_text.value) @@ -608,6 +625,7 @@ def test_DDM_size_too_long(): rate=-5.0, time_step_size=1.0 ), + execute_until_finished=False, ) assert "is greater than 1, implying there are" in str(error_text.value) @@ -621,7 +639,8 @@ def test_DDM_time(): rate=-5.0, time_step_size=0.2, non_decision_time=0.5 - ) + ), + execute_until_finished=False, ) time_0 = D.function.previous_time # t_0 = 0.5 @@ -663,6 +682,8 @@ def test_DDM_in_composition(benchmark, mode): non_decision_time=0.0, time_step_size=0.1, ), + execute_until_finished=False, + reset_stateful_function_when=Never() ) C = pnl.Composition() C.add_linear_processing_pathway([M]) @@ -729,8 +750,8 @@ def test_DDM_threshold_modulation(mode): ]) def test_ddm_is_finished(mode, noise, threshold, expected_results): comp = Composition() - ddm = DDM(execute_until_finished=True, - function=DriftDiffusionIntegrator(threshold=threshold, noise=noise)) + ddm = DDM(function=DriftDiffusionIntegrator(threshold=threshold, noise=np.sqrt(noise), time_step_size=1.0), + execute_until_finished=True) comp.add_node(ddm) results = comp.run([0], bin_execute=mode) @@ -804,7 +825,8 @@ def test_sequence_of_DDM_mechs_in_Composition_Pathway(): pytest.param('PTXExec', marks=[pytest.mark.llvm, pytest.mark.cuda]), pytest.param('PTXRun', marks=[pytest.mark.llvm, pytest.mark.cuda])]) def test_DDMMechanism_LCA_equivalent(mode): - ddm = DDM(default_variable=[0], function=DriftDiffusionIntegrator(rate=1, time_step_size=0.1)) + ddm = DDM(default_variable=[0], function=DriftDiffusionIntegrator(rate=1, time_step_size=0.1), + execute_until_finished=False) comp2 = Composition() comp2.add_node(ddm) result2 = comp2.run(inputs={ddm:[1]}, bin_execute=mode) From ec2142aa3c1c7230aaa8e96b54a4a972fb01e73d Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 23 Feb 2021 14:32:25 -0500 Subject: [PATCH 025/285] Another fix for switching default time_step_size on DriftDiffusionIntegrator --- tests/mechanisms/test_integrator_mechanism.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mechanisms/test_integrator_mechanism.py b/tests/mechanisms/test_integrator_mechanism.py index b7d15b76d68..6cdd56dcc4d 100644 --- a/tests/mechanisms/test_integrator_mechanism.py +++ b/tests/mechanisms/test_integrator_mechanism.py @@ -793,7 +793,7 @@ def test_integrator_type_diffusion_rate_float(self): I = IntegratorMechanism( name='IntegratorMechanism', function=DriftDiffusionIntegrator( - rate=5.0 + rate=5.0, time_step_size=1.0 ) ) # P = Process(pathway=[I]) From 3c135a737708dc7b01ccacfa423bf00dcd05fc14 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 24 Feb 2021 13:03:27 -0500 Subject: [PATCH 026/285] More ddm test fixes --- tests/composition/test_composition.py | 12 ++++++------ tests/functions/test_integrator.py | 7 ++++++- tests/mechanisms/test_integrator_mechanism.py | 3 ++- tests/mechanisms/test_mechanisms.py | 2 +- tests/mechanisms/test_processing_mechanism.py | 2 +- tests/scheduling/test_scheduler.py | 12 ++++++++---- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index af7b5fdf281..b50efaf3181 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -3753,7 +3753,7 @@ def test_run_recurrent_transfer_mechanism_integrator_2(self, benchmark, mode): benchmark(comp.execute, inputs={R: [[1.0, 2.0]]}, bin_execute=mode) def test_run_termination_condition_custom_context(self): - D = pnl.DDM(function=pnl.DriftDiffusionIntegrator) + D = pnl.DDM(function=pnl.DriftDiffusionIntegrator, execute_until_finished=False) comp = pnl.Composition() comp.add_node(node=D) @@ -6474,7 +6474,7 @@ def test_save_state_before_simulations(self): integration_rate=0.2 ) - B = IntegratorMechanism(name='B', function=DriftDiffusionIntegrator(rate=0.1)) + B = IntegratorMechanism(name='B', function=DriftDiffusionIntegrator(rate=0.1, time_step_size=1.0)) C = TransferMechanism(name='C') comp = Composition() @@ -6989,8 +6989,8 @@ def test_danglingControlledMech(self): drift_rate=(1.0), threshold=(0.1654), noise=(0.5), - starting_point=(0), - t0=0.25, + starting_value=(0), + non_decision_time=0.25, ), name='Decision', ) @@ -7019,8 +7019,8 @@ def test_danglingControlledMech(self): ), ), noise=(0.5), - starting_point=(0), - t0=0.45 + starting_value=(0), + non_decision_time=0.45 ), name='second_DDM', ) diff --git a/tests/functions/test_integrator.py b/tests/functions/test_integrator.py index 05f0a92d5e7..51f0caad63e 100644 --- a/tests/functions/test_integrator.py +++ b/tests/functions/test_integrator.py @@ -141,7 +141,12 @@ def test_execute(func, mode, variable, noise, params, benchmark): if func[1] == DriftIntFun: pytest.skip("DriftDiffusionIntegrator doesn't support functional noise") - f = func[0](default_variable=variable, noise=noise, **params) + # If we are dealing with a DriftDiffusionIntegrator, noise and time_step_size defaults + # have changed since this test was created. Hard code their old values. + if 'DriftDiffusionIntegrator' in str(func[0]): + f = func[0](default_variable=variable, noise=np.sqrt(noise), time_step_size=1.0, **params) + else: + f = func[0](default_variable=variable, noise=noise, **params) if mode == 'Python': ex = f diff --git a/tests/mechanisms/test_integrator_mechanism.py b/tests/mechanisms/test_integrator_mechanism.py index 6cdd56dcc4d..afd47906c91 100644 --- a/tests/mechanisms/test_integrator_mechanism.py +++ b/tests/mechanisms/test_integrator_mechanism.py @@ -1173,7 +1173,8 @@ def test_integrator_drift_diffusion_noise_val(self): I = IntegratorMechanism( name='IntegratorMechanism', function=DriftDiffusionIntegrator( - noise=5.0, + noise=np.sqrt(5.0), + time_step_size=1.0, ), ) diff --git a/tests/mechanisms/test_mechanisms.py b/tests/mechanisms/test_mechanisms.py index 4bd4b8bc9d0..cfe775fc12c 100644 --- a/tests/mechanisms/test_mechanisms.py +++ b/tests/mechanisms/test_mechanisms.py @@ -155,7 +155,7 @@ def test_function_parameter_assignment(self, param_name, function): class TestResetValues: def test_reset_state_integrator_mechanism(self): - A = pnl.IntegratorMechanism(name='A', function=pnl.DriftDiffusionIntegrator()) + A = pnl.IntegratorMechanism(name='A', function=pnl.DriftDiffusionIntegrator(time_step_size=1.0)) # Execute A twice # [0] saves decision variable only (not time) diff --git a/tests/mechanisms/test_processing_mechanism.py b/tests/mechanisms/test_processing_mechanism.py index 7377306d5f4..a4cf69c359f 100644 --- a/tests/mechanisms/test_processing_mechanism.py +++ b/tests/mechanisms/test_processing_mechanism.py @@ -74,7 +74,7 @@ def test_processing_mechanism_linear_function(self): (SoftMax, [[1,]]), (SimpleIntegrator, [[1.]]), (AdaptiveIntegrator, [[1.]]), - (DriftDiffusionIntegrator, [[[1.]], [[1.]]]), + (DriftDiffusionIntegrator(time_step_size=1.0), [[[1.]], [[1.]]]), (OrnsteinUhlenbeckIntegrator, [[[-1.]], [[1.]]]), (AccumulatorIntegrator, [[0.]]), (FitzHughNagumoIntegrator, [[[0.05127053]], [[0.00279552]], [[0.05]]]), diff --git a/tests/scheduling/test_scheduler.py b/tests/scheduling/test_scheduler.py index df9d1b7778d..11e82fc51e4 100644 --- a/tests/scheduling/test_scheduler.py +++ b/tests/scheduling/test_scheduler.py @@ -12,7 +12,7 @@ from psyneulink.core.globals.context import Context from psyneulink.core.globals.keywords import VALUE from psyneulink.core.scheduling.condition import AfterNCalls, AfterNPasses, AfterNTrials, AfterPass, All, AllHaveRun, Always, Any, AtPass, BeforeNCalls, BeforePass, \ - EveryNCalls, EveryNPasses, JustRan, WhenFinished + EveryNCalls, EveryNPasses, JustRan, WhenFinished, Never from psyneulink.core.scheduling.scheduler import Scheduler from psyneulink.core.scheduling.time import TimeScale from psyneulink.library.components.mechanisms.processing.integrator.ddm import DDM @@ -159,7 +159,9 @@ def test_one_composition_two_contexts(self): assert output == pytest.helpers.setify_expected_output(expected_output) def test_change_termination_condition(self): - D = DDM(function=DriftDiffusionIntegrator(threshold=10)) + D = DDM(function=DriftDiffusionIntegrator(threshold=10, time_step_size=1.0), + execute_until_finished=False, + reset_stateful_function_when=Never()) C = Composition(pathways=[D]) D.set_log_conditions(VALUE) @@ -1570,10 +1572,12 @@ def test_time_termination_measures(self, mode, timescale, expected): ]) def test_scheduler_conditions(self, mode, condition, scale, expected_result): decisionMaker = pnl.DDM( - function=pnl.DriftDiffusionIntegrator(starting_point=0, + function=pnl.DriftDiffusionIntegrator(starting_value=0, threshold=1, - noise=0.0), + noise=0.0, + time_step_size=1.0), reset_stateful_function_when=pnl.AtTrialStart(), + execute_until_finished=False, output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], name='DDM') From 7208c19c0f73ea18607a70ab40291eb7a0d757d7 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 24 Feb 2021 13:04:55 -0500 Subject: [PATCH 027/285] Changed dependency fastkde to psyneulink-fastkde. This is a termporary fork of fastkde that I setup because wheels are not available on fastkde. I will reach out fastkde's developers and see if they are willing to upstream my Github action that builds wheels for Python 3.6-3.9 on windows, macos, and linux platforms. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8c94d72a682..eb0de575128 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,5 +15,5 @@ typecheck-decorator<=1.2 leabra-psyneulink<=0.3.2 rich<=9.11.0 pandas<=1.0.5 -fastkde +psyneulink-fastkde From 00609bacfae72c4430b07e516ab24d19518d162c Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 24 Feb 2021 13:51:30 -0500 Subject: [PATCH 028/285] fix: pycodestyle --- .../core/components/functions/fitfunctions.py | 231 +++++++++++------- 1 file changed, 149 insertions(+), 82 deletions(-) diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py index e9e5ea5dc37..77767eacd65 100644 --- a/psyneulink/core/components/functions/fitfunctions.py +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -1,3 +1,13 @@ +from fastkde import fastKDE +from scipy.interpolate import interpn +from scipy.optimize import differential_evolution + +from psyneulink.core.globals.context import Context +from psyneulink.core.globals.parameters import Parameter +from psyneulink.core.scheduling.condition import AtTrialStart +from psyneulink.core.globals.parameters import Parameter + + import typing import time import numpy as np @@ -7,18 +17,8 @@ import warnings import logging -logger = logging.getLogger(__name__) -from psyneulink.core.globals.context import Context -from psyneulink.core.globals.parameters import Parameter -from psyneulink.core.scheduling.condition import AtTrialStart -from psyneulink.core.globals.parameters import Parameter -from psyneulink.core.components.functions.optimizationfunctions import OBJECTIVE_FUNCTION, SEARCH_SPACE, \ - OptimizationFunction - -from fastkde import fastKDE -from scipy.interpolate import interpn -from scipy.optimize import differential_evolution +logger = logging.getLogger(__name__) def get_param_str(params): @@ -34,7 +34,7 @@ def get_param_str(params): The string version of the parameter dict """ - return ", ".join(f'{name}={value:.5f}' for name, value in params.items()) + return ", ".join(f"{name}={value:.5f}" for name, value in params.items()) class BadLikelihoodWarning(UserWarning): @@ -43,13 +43,13 @@ class BadLikelihoodWarning(UserWarning): This is usually caused when parameter values cause degenerate simulation results (no variance in values). It can also be caused when experimental data is not matching any of the simulation results. """ + pass -def simulation_likelihood(sim_data, - exp_data=None, - categorical_dims=None, - combine_trials=False): +def simulation_likelihood( + sim_data, exp_data=None, categorical_dims=None, combine_trials=False +): """ Compute the likelihood of a simulation dataset (or the parameters that generated it) conditional on a set of experimental data. This function essentially just computes the kernel density estimate (KDE) @@ -135,12 +135,19 @@ def simulation_likelihood(sim_data, # If any dimension of the data has a 0 range (all are same value) then # this will cause problems doing the KDE, skip. - data_range = np.max(dsub) - np.min(dsub) if dsub.ndim == 1 else np.amax(dsub, 1) - np.amin(dsub, 1) + data_range = ( + np.max(dsub) - np.min(dsub) + if dsub.ndim == 1 + else np.amax(dsub, 1) - np.amin(dsub, 1) + ) if np.any(data_range == 0): dens_u[category] = (None, None) - warnings.warn(BadLikelihoodWarning( - f"Could not perform kernel density estimate. Range of simulation data was 0 for at least " - f"one dimension. Range={data_range}")) + warnings.warn( + BadLikelihoodWarning( + f"Could not perform kernel density estimate. Range of simulation data was 0 for at least " + f"one dimension. Range={data_range}" + ) + ) continue # Do KDE @@ -149,7 +156,7 @@ def simulation_likelihood(sim_data, axes = fKDE.axes # Scale the pdf by the fraction of simulations that fall within this category - pdf = pdf * (len(dsub)/len(s)) + pdf = pdf * (len(dsub) / len(s)) # Save the KDE values and axes for this category dens_u[category] = (pdf, axes) @@ -182,21 +189,29 @@ def simulation_likelihood(sim_data, # Linear interpolation using the grid we computed the KDE # on. if kde is not None: - kdes_eval[trial] = interpn(axes, kde, exp_data[trial, ~categorical_dims], - method='linear', bounds_error=False, fill_value=ZERO_PROB) + kdes_eval[trial] = interpn( + axes, + kde, + exp_data[trial, ~categorical_dims], + method="linear", + bounds_error=False, + fill_value=ZERO_PROB, + ) else: kdes_eval[trial] = ZERO_PROB - # Check to see if any of the trials have non-zero likelihood, if not, something is probably wrong # and we should warn the user. if np.alltrue(kdes_eval == ZERO_PROB): - warnings.warn(BadLikelihoodWarning( - "Evaluating likelihood generated by simulation data resulted in zero values for all trials " - "of experimental data. This means the model is not generating data similar to your " - "experimental data. If you have categorical dimensions, make sure values match exactly to " - "output values of the composition. Also make sure parameter ranges you are searching over " - "are reasonable for your data.")) + warnings.warn( + BadLikelihoodWarning( + "Evaluating likelihood generated by simulation data resulted in zero values for all trials " + "of experimental data. This means the model is not generating data similar to your " + "experimental data. If you have categorical dimensions, make sure values match exactly to " + "output values of the composition. Also make sure parameter ranges you are searching over " + "are reasonable for your data." + ) + ) return kdes_eval @@ -204,14 +219,16 @@ def simulation_likelihood(sim_data, return kdes -def make_likelihood_function(composition: 'psyneulink.core.composition.Composition', - fit_params: typing.List[Parameter], - inputs: typing.Union[np.ndarray, typing.List], - data_to_fit: typing.Union[np.ndarray, pd.DataFrame], - categorical_dims: typing.Union[np.ndarray, None] = None, - num_sims_per_trial: int = 1000, - fixed_params: typing.Optional[typing.Dict[Parameter, typing.Any]] = None, - combine_trials=True): +def make_likelihood_function( + composition: "psyneulink.core.composition.Composition", + fit_params: typing.List[Parameter], + inputs: typing.Union[np.ndarray, typing.List], + data_to_fit: typing.Union[np.ndarray, pd.DataFrame], + categorical_dims: typing.Union[np.ndarray, None] = None, + num_sims_per_trial: int = 1000, + fixed_params: typing.Optional[typing.Dict[Parameter, typing.Any]] = None, + combine_trials=True, +): """ Construct and return a Python function that returns the log likelihood of experimental data being generated by a PsyNeuLink composition. The likelihood function is parameterized @@ -261,8 +278,14 @@ def make_likelihood_function(composition: 'psyneulink.core.composition.Compositi # If we have multiple parameters with this name already, assign it the # next available number all_param_names = [p.name for p in fit_params] - dupe_counts = [all_param_names[:i].count(all_param_names[i])+1 for i in range(len(all_param_names))] - all_param_names = [name if count == 1 else f"{name}_{count}" for name, count in zip(all_param_names, dupe_counts)] + dupe_counts = [ + all_param_names[:i].count(all_param_names[i]) + 1 + for i in range(len(all_param_names)) + ] + all_param_names = [ + name if count == 1 else f"{name}_{count}" + for name, count in zip(all_param_names, dupe_counts) + ] param_name_map = dict(zip(fit_params, all_param_names)) if type(data_to_fit) == np.ndarray: @@ -274,7 +297,9 @@ def make_likelihood_function(composition: 'psyneulink.core.composition.Compositi categorical_dims = [False for i in range(data_to_fit.shape[1])] elif type(data_to_fit) == pd.DataFrame: - categorical_dims = [data_to_fit[c].dtype.name == "category" for c in data_to_fit.columns] + categorical_dims = [ + data_to_fit[c].dtype.name == "category" for c in data_to_fit.columns + ] else: raise ValueError("data_to_fit must be a 2D numpy array or a Pandas DataFrame") @@ -293,7 +318,9 @@ def log_likelihood(**kwargs): try: value = kwargs[param_name_map[param]] except KeyError: - raise ValueError(f"No argument {param_name_map[param]} passed to likelihood function for Parameter: \n{param}") + raise ValueError( + f"No argument {param_name_map[param]} passed to likelihood function for Parameter: \n{param}" + ) # FIXME: DDM requires that starting_value is an array in compiled mode for some reason. Ugly hack! if param.name == "starting_value" and type(value) != np.ndarray: @@ -312,10 +339,12 @@ def log_likelihood(**kwargs): # Run the composition for all simulations, this corresponds to looping over the input # num_simulations times. - composition.run(inputs=inputs, - num_trials=num_sims_per_trial * num_trials, - bin_execute=True, - context=context) + composition.run( + inputs=inputs, + num_trials=num_sims_per_trial * num_trials, + bin_execute=True, + context=context, + ) results = np.squeeze(np.array(composition.results)) @@ -323,10 +352,12 @@ def log_likelihood(**kwargs): sim_data = np.array(np.vsplit(results, num_trials)) # Compute the likelihood given the data - like = simulation_likelihood(sim_data=sim_data, - exp_data=data_to_fit.to_numpy().astype(float), - categorical_dims=categorical_dims, - combine_trials=combine_trials) + like = simulation_likelihood( + sim_data=sim_data, + exp_data=data_to_fit.to_numpy().astype(float), + categorical_dims=categorical_dims, + combine_trials=combine_trials, + ) # Make 0 densities very small so log doesn't explode like[like == 0.0] = 1.0e-10 @@ -341,19 +372,21 @@ class MaxLikelihoodEstimator: Implements a maximum likelihood estimation given a likelihood function """ - def __init__(self, - log_likelihood_function: typing.Callable, - fit_params_bounds: typing.Dict[str, typing.Tuple]): + def __init__( + self, + log_likelihood_function: typing.Callable, + fit_params_bounds: typing.Dict[str, typing.Tuple], + ): self.log_likelihood_function = log_likelihood_function self.fit_params_bounds = fit_params_bounds # Setup a named tuple to store records for each iteration if the user requests it - self.IterRecord = collections.namedtuple('IterRecord', - f"{' '.join(self.fit_params_bounds.keys())} neg_log_likelihood likelihood_eval_time") + self.IterRecord = collections.namedtuple( + "IterRecord", + f"{' '.join(self.fit_params_bounds.keys())} neg_log_likelihood likelihood_eval_time", + ) - def fit(self, - display_iter: bool = False, - save_iterations: bool = False): + def fit(self, display_iter: bool = False, save_iterations: bool = False): bounds = list(self.fit_params_bounds.values()) @@ -363,12 +396,14 @@ def fit(self, iterations = [] with Progress( - "[progress.description]{task.description}", - BarColumn(), - "Convergence: [progress.percentage]{task.percentage:>3.0f}%", - TimeRemainingColumn(), + "[progress.description]{task.description}", + BarColumn(), + "Convergence: [progress.percentage]{task.percentage:>3.0f}%", + TimeRemainingColumn(), ) as progress: - opt_task = progress.add_task("Maximum likelihood optimization ...", total=100, start=False) + opt_task = progress.add_task( + "Maximum likelihood optimization ...", total=100, start=False + ) warns_with_params = [] with warnings.catch_warnings(record=True) as warns: @@ -388,20 +423,36 @@ def neg_log_like(x): if display_iter: # If we got a warning generating the likelihood, report it - if len(warns) > 0 and warns[-1].category == BadLikelihoodWarning: + if ( + len(warns) > 0 + and warns[-1].category == BadLikelihoodWarning + ): progress.console.print(f"Warning: ", style="bold red") - progress.console.print(f"{warns[-1].message}", style="bold red") - progress.console.print(f"{get_param_str(params)}, Neg-Log-Likelihood: {p}, " - f"Likelihood-Eval-Time: {elapsed} (seconds)", style="bold red") + progress.console.print( + f"{warns[-1].message}", style="bold red" + ) + progress.console.print( + f"{get_param_str(params)}, Neg-Log-Likelihood: {p}, " + f"Likelihood-Eval-Time: {elapsed} (seconds)", + style="bold red", + ) # Clear the warnings warns.clear() else: - progress.console.print(f"{get_param_str(params)}, Neg-Log-Likelihood: {p}, " - f"Likelihood-Eval-Time: {elapsed} (seconds)") + progress.console.print( + f"{get_param_str(params)}, Neg-Log-Likelihood: {p}, " + f"Likelihood-Eval-Time: {elapsed} (seconds)" + ) # Are we saving each iteration if save_iterations: - iterations.append(self.IterRecord(**params, neg_log_likelihood=p, likelihood_eval_time=elapsed)) + iterations.append( + self.IterRecord( + **params, + neg_log_likelihood=p, + likelihood_eval_time=elapsed, + ) + ) return p @@ -409,34 +460,50 @@ def progress_callback(x, convergence): progress.start_task(opt_task) params = dict(zip(self.fit_params_bounds.keys(), x)) convergence_pct = 100.0 * convergence - progress.console.print(f"[green]Current Best Parameters: {get_param_str(params)}, Neg-Log-Likelihood: {neg_log_like(x)}") + progress.console.print( + f"[green]Current Best Parameters: {get_param_str(params)}, Neg-Log-Likelihood: {neg_log_like(x)}" + ) # If we encounter any BadLikelihoodWarnings. Summarize them for the user if len(warns_with_params) > 0: - progress.console.print("Warning: degenerate likelihood for the following parameter values ", style='bold red') + progress.console.print( + "Warning: degenerate likelihood for the following parameter values ", + style="bold red", + ) for w in warns_with_params: - progress.console.print(f"\t{get_param_str(w[1])}", style='bold red') - progress.console.print("If these warnings are intermittent, check to see if search " - "space is appropriately bounded. If they are constant, make sure " - "experimental data and output of your composition are similar.", - style='bold red') + progress.console.print( + f"\t{get_param_str(w[1])}", style="bold red" + ) + progress.console.print( + "If these warnings are intermittent, check to see if search " + "space is appropriately bounded. If they are constant, make sure " + "experimental data and output of your composition are similar.", + style="bold red", + ) progress.update(opt_task, completed=convergence_pct) - r = differential_evolution(neg_log_like, bounds, callback=progress_callback, maxiter=500, polish=False) + r = differential_evolution( + neg_log_like, + bounds, + callback=progress_callback, + maxiter=500, + polish=False, + ) # Bind the fitted parameters to their names fitted_params = dict(zip(list(self.fit_params_bounds.keys()), r.x)) # Save all the results output_dict = { - 'fitted_params': fitted_params, - 'neg-log-likelihood': r.fun, + "fitted_params": fitted_params, + "neg-log-likelihood": r.fun, } # Return a record for each iteration if we were supposed to. if save_iterations: - output_dict['iterations'] = pd.DataFrame.from_records(iterations, columns=self.IterRecord._fields) + output_dict["iterations"] = pd.DataFrame.from_records( + iterations, columns=self.IterRecord._fields + ) return output_dict - From 9c862233991a75d804b96d168b91acce973feeb4 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 2 Mar 2021 15:44:10 -0500 Subject: [PATCH 029/285] Implement a DMM in pytorch --- Scripts/Debug/ddm/ddm_fit.py | 3 +- Scripts/Debug/ddm/pytorch_ddm.py | 112 +++++++++++++++++++++++++++++++ Scripts/Debug/ddm/taichi_ddm.py | 33 +++++++++ Scripts/Debug/ddm/taichi_test.py | 4 +- requirements.txt | 2 +- 5 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 Scripts/Debug/ddm/pytorch_ddm.py diff --git a/Scripts/Debug/ddm/ddm_fit.py b/Scripts/Debug/ddm/ddm_fit.py index 409e54420c6..b6439f18dee 100644 --- a/Scripts/Debug/ddm/ddm_fit.py +++ b/Scripts/Debug/ddm/ddm_fit.py @@ -14,8 +14,7 @@ output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], name='DDM') -comp = pnl.Composition() -comp.add_node(decision) +comp = pnl.Composition(pathways=decision) #%% diff --git a/Scripts/Debug/ddm/pytorch_ddm.py b/Scripts/Debug/ddm/pytorch_ddm.py new file mode 100644 index 00000000000..0581b3ad862 --- /dev/null +++ b/Scripts/Debug/ddm/pytorch_ddm.py @@ -0,0 +1,112 @@ +#%% +import time +import torch +from torch import nn + +if torch.cuda.is_available(): + dev = "cuda:0" +else: + dev = "cpu" + + +def simulate_ddms(starting_value: float = 0.0, + rate: float = 1.0, + non_decision_time: float = 0.0, + threshold: float = 1.0, + noise: float = 1.0, + time_step_size: float = 0.01, + num_walkers: int = 1000, + dev: str = "cuda:0"): + """ + A simulation of + + Args: + starting_value: + rate: + non_decision_time: + threshold: + noise: + time_step_size: + num_walkers: + dev: + + Returns: + + """ + + particle = torch.full(size=(num_walkers,), fill_value=starting_value, device=dev) + active = torch.ones(size=(num_walkers,), dtype=torch.bool, device=dev) + rts = torch.zeros(size=(num_walkers,), device=dev) + + for i in range(3000): + #dw = torch.distributions.Normal(loc=rate * time_step_size * active, scale=noise * active).rsample() + dw = torch.normal(mean=rate * time_step_size * active, std=noise * active) + particle = particle + dw * torch.sqrt(time_step_size) + rts = rts + active + active = torch.abs(particle) < threshold + + rts = (non_decision_time + rts * time_step_size) + + decisions = torch.ones(size=(num_walkers,), device=dev) + decisions[particle <= -threshold] = 0 + + return rts, decisions + +ddm_params = dict(starting_value=0.0, rate=0.3, non_decision_time=0.15, threshold=0.6, noise=1.0, time_step_size=0.001) + +NUM_WALKERS = 1000000 + +# Move params to device +for key, val in ddm_params.items(): + ddm_params[key] = torch.tensor(val).to(dev) + +#%% + +t0 = time.time() +rts, decision = simulate_ddms(**ddm_params, num_walkers=NUM_WALKERS, dev=dev) +rts = rts.to("cpu") +decision = decision.to("cpu") +print(f"PyTorch Elapsed: {time.time() - t0}") + +#%% + +# JIT +jit_simulate_ddms = torch.jit.script(simulate_ddms) + +#%% +rts, decision = jit_simulate_ddms(**ddm_params, num_walkers=NUM_WALKERS) + +NUM_TIMES = 50 +t0 = time.time() +for i in range(NUM_TIMES): + rts, decision = jit_simulate_ddms(**ddm_params, num_walkers=NUM_WALKERS) + rts = rts.to("cpu") + decision = decision.to("cpu") +print(f"JIT Elapsed: {1000 * ((time.time() - t0) / NUM_TIMES)} milliseconds") + +#%% +class DriftDiffusionModel(torch.nn.Module): + def forward(self, + starting_value, + rate, + non_decision_time, + threshold, + noise, + time_step_size, + num_walkers): + return simulate_ddms(starting_value, rate, non_decision_time, threshold, noise, time_step_size, num_walkers) + +#%% +ddm_model = torch.jit.script(DriftDiffusionModel()) + +with open('ddm.onnx', 'wb') as file: + torch.onnx.export(model=ddm_model, + args=tuple(ddm_params.values()) + (torch.tensor(NUM_WALKERS),), + example_outputs=(rts, decision), + f=file, + verbose=True, + opset_version=11) + +#%% +import seaborn as sns +sns.kdeplot(rts) diff --git a/Scripts/Debug/ddm/taichi_ddm.py b/Scripts/Debug/ddm/taichi_ddm.py index b012113c5a0..de8619cd332 100644 --- a/Scripts/Debug/ddm/taichi_ddm.py +++ b/Scripts/Debug/ddm/taichi_ddm.py @@ -1,6 +1,8 @@ import taichi as ti import taichi_utils as tu +ti.init(arch=ti.gpu) + num_simulations = 1000000 rt = ti.field(ti.f32, num_simulations) decision = ti.field(ti.i32, num_simulations) @@ -35,3 +37,34 @@ def simulate_many_ddms(starting_value: ti.f32, for i in rt: rt[i], decision[i] = simulate_ddm(starting_value, non_decision_time, drift_rate, threshold, time_step_size) + + +if __name__ == "__main__": + + ddm_params = dict(starting_value=0.0, non_decision_time=0.15, drift_rate=0.3, threshold=0.6, time_step_size=0.001) + + simulate_many_ddms(*list(ddm_params.values())) + rts = rt.to_numpy() + choices = decision.to_numpy() + + ti.sync() + rts = rt.to_numpy() + choices = decision.to_numpy() + valid = rts > 0.0 + rts = rts[valid] + choices = choices[valid] + + import time + t0 = time.time() + + NUM_TIMES = 50 + for i in range(NUM_TIMES): + simulate_many_ddms(*list(ddm_params.values())) + ti.sync() + rts = rt.to_numpy() + choices = decision.to_numpy() + valid = rts > 0.0 + rts = rts[valid] + choices = choices[valid] + + print(f"Elapsed: { 1000*((time.time() - t0) / NUM_TIMES)} milliseconds") \ No newline at end of file diff --git a/Scripts/Debug/ddm/taichi_test.py b/Scripts/Debug/ddm/taichi_test.py index 4b0c8b1c2c1..ec7b8efd86e 100644 --- a/Scripts/Debug/ddm/taichi_test.py +++ b/Scripts/Debug/ddm/taichi_test.py @@ -136,8 +136,8 @@ def simulate_many_lcas(competition: ti.f32, non_decistion_time=0.3, time_step_size=0.0001) -#simulate_many_ddms(*list(ddm_params.values())) -simulate_many_lcas(*list(lca_params.values())) +simulate_many_ddms(*list(ddm_params.values())) +#simulate_many_lcas(*list(lca_params.values())) rts = rt.to_numpy() choices = decision.to_numpy() diff --git a/requirements.txt b/requirements.txt index eb0de575128..2cace8011f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,5 +15,5 @@ typecheck-decorator<=1.2 leabra-psyneulink<=0.3.2 rich<=9.11.0 pandas<=1.0.5 -psyneulink-fastkde +fastkde>=1.0.18 From d503ae66d0ee0ecf0417e113b8c4ed8570ba97bd Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 3 Mar 2021 18:22:28 -0500 Subject: [PATCH 030/285] Fixed DDM tests in compiled mode --- .../statefulfunctions/integratorfunctions.py | 3 --- .../mechanisms/processing/integrator/ddm.py | 22 +++++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index 05e97dfa128..dc081b8c83a 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -2522,13 +2522,10 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): val = builder.fmul(val, time_step_size) val = builder.fadd(val, prev_val) - factor = time_step_size sqrt_f = ctx.get_builtin("sqrt", [ctx.float_ty]) factor = builder.call(sqrt_f, [time_step_size]) - factor = builder.fmul(noise, factor) factor = builder.fmul(rand_val, factor) - factor = builder.fmul(factor, noise) val = builder.fadd(val, factor) diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index 366fc4c9f00..d805575bf4b 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -765,6 +765,17 @@ def __init__(self, prefs: tc.optional(is_pref_set) = None, **kwargs): + # New (1/19/2021) default behavior of DDM mechanism is to reset stateful functions + # on each new trial. + self.reset_stateful_function_when = kwargs.get('reset_stateful_function_when', AtTrialStart()) + + # New (1/19/2021) default behaviour of DDM mechanism is to execute until finished. That + # is, it should execute until it reaches its threshold. + self.execute_until_finished = kwargs.get('execute_until_finished', True) + + # FIXME: Set maximum executions absurdly large to avoid early termination + self.max_executions_before_finished = sys.maxsize + # Override instantiation of StandardOutputPorts usually done in _instantiate_output_ports # in order to use SEQUENTIAL indices self.standard_output_ports = StandardOutputPorts(self, self.standard_output_ports, indices=SEQUENTIAL) @@ -863,17 +874,6 @@ def __init__(self, self._instantiate_plotting_functions() - # New (1/19/2021) default behavior of DDM mechanism is to reset stateful functions - # on each new trial. - self.reset_stateful_function_when = kwargs.get('reset_stateful_function_when', AtTrialStart()) - - # New (1/19/2021) default behaviour of DDM mechanism is to execute until finished. That - # is, it should execute until it reaches its threshold. - self.execute_until_finished = kwargs.get('execute_until_finished', True) - - # FIXME: Set maximum executions absurdly large to avoid early termination - self.max_executions_before_finished = sys.maxsize - def plot(self, stimulus=1.0, threshold=10.0): """ Generate a dynamic plot of the DDM integrating over time towards a threshold. From 4016a70f233b31477257b4e2e87dd20f22746173 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 3 Mar 2021 19:03:45 -0500 Subject: [PATCH 031/285] Force numpy < 1.20 for Python 3.6, fastkde wheels require this --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 59fee92cf5d..65b032847ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,8 @@ grpcio-tools<1.35.0 llvmlite<0.36 matplotlib<3.3.4 networkx<2.6 -numpy>=1.20,<1.20.2 +numpy>=1.20.0,<1.20.2; python_version > '3.6' +numpy<1.20; python_version == '3.6' pillow<8.1.0 toposort<1.7 torch<1.8.0; sys_platform != 'win32' and platform_machine == 'x86_64' and platform_python_implementation == 'CPython' From 211d7af3fae49cfe62545697aac4056689573008 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 4 Mar 2021 15:18:55 -0500 Subject: [PATCH 032/285] fix: use new ExecutionMode --- .gitignore | 3 + Scripts/Debug/ddm/ddm_fit.py | 2 +- Scripts/Debug/ddm/ddm_plot_check.py | 2 +- .../hackathon/stability_flexibility_csi.py | 274 ------------------ .../core/components/functions/fitfunctions.py | 3 +- .../mechanisms/processing/integrator/ddm.py | 23 +- 6 files changed, 18 insertions(+), 289 deletions(-) delete mode 100644 Scripts/Debug/hackathon/stability_flexibility_csi.py diff --git a/.gitignore b/.gitignore index caca3b3f6ee..654943bed71 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ # Log files created by SLURM jobs in this directory Scripts/Debug/predator_prey_opt/logs/ +# Any plots generated from testing DDMs +Scripts/Debug/ddm/*.png + ### OSX ### *.DS_Store .AppleDouble diff --git a/Scripts/Debug/ddm/ddm_fit.py b/Scripts/Debug/ddm/ddm_fit.py index b6439f18dee..f0084076e11 100644 --- a/Scripts/Debug/ddm/ddm_fit.py +++ b/Scripts/Debug/ddm/ddm_fit.py @@ -26,7 +26,7 @@ # Run the composition to generate some data to fit comp.run(inputs=inputs_dict, num_trials=len(input), - bin_execute=True) + execution_mode=pnl.ExecutionMode.LLVMRun) # Store the results of this "experiment" as a numpy array. This should be a # 2D array of shape (len(input), 2). The first column being a discrete variable diff --git a/Scripts/Debug/ddm/ddm_plot_check.py b/Scripts/Debug/ddm/ddm_plot_check.py index 5aea1660f66..fba891d5006 100644 --- a/Scripts/Debug/ddm/ddm_plot_check.py +++ b/Scripts/Debug/ddm/ddm_plot_check.py @@ -56,7 +56,7 @@ def ddm_pdf_simulate(drift_rate=0.75, threshold=1.0, noise=0.1, starting_point=0 comp.run(inputs={decision: input}, num_trials=num_samples * len(input), - bin_execute=True, + execution_mode=pnl.ExecutionMode.LLVMRun, context=context) results = np.squeeze(np.array(comp.results)) diff --git a/Scripts/Debug/hackathon/stability_flexibility_csi.py b/Scripts/Debug/hackathon/stability_flexibility_csi.py deleted file mode 100644 index 1693cab5005..00000000000 --- a/Scripts/Debug/hackathon/stability_flexibility_csi.py +++ /dev/null @@ -1,274 +0,0 @@ -import psyneulink as pnl -import psyneulink.core.llvm as pnlvm - -import numpy as np -import random -import pytest -import pandas as pd - - -# Define function to generate a counterbalanced trial sequence with a specified switch trial frequency -def generateTrialSequence(N, Frequency): - - # Compute trial number - nTotalTrials = N - switchFrequency = Frequency - - nSwitchTrials = int(nTotalTrials * switchFrequency) - nRepeatTrials = int(nTotalTrials - nSwitchTrials) - - # Determine task transitions - transitions = [1] * nSwitchTrials + [0] * nRepeatTrials - order = np.random.permutation(list(range(nTotalTrials))) - transitions[:] = [transitions[i] for i in order] - - # Determine stimuli with 50% congruent trials - stimuli = [[1, 1]] * int(nSwitchTrials/4) + [[1, -1]] * int(nSwitchTrials/4) + [[-1, -1]] * int(nSwitchTrials/4) + [[-1, 1]] * int(nSwitchTrials/4) + \ - [[1, 1]] * int(nRepeatTrials/4) + [[1, -1]] * int(nRepeatTrials/4) + [[-1, -1]] * int(nRepeatTrials/4) + [[-1, 1]] * int(nRepeatTrials/4) - stimuli[:] = [stimuli[i] for i in order] - - # Determine cue-stimulus intervals - CSI = [60] * int(nSwitchTrials / 8) + [80] * int(nSwitchTrials / 8) + \ - [60] * int(nSwitchTrials / 8) + [80] * int(nSwitchTrials / 8) + \ - [60] * int(nSwitchTrials / 8) + [80] * int(nSwitchTrials / 8) + \ - [60] * int(nSwitchTrials / 8) + [80] * int(nSwitchTrials / 8) + \ - [60] * int(nRepeatTrials / 8) + [80] * int(nRepeatTrials / 8) + \ - [60] * int(nRepeatTrials / 8) + [80] * int(nRepeatTrials / 8) + \ - [60] * int(nRepeatTrials / 8) + [80] * int(nRepeatTrials / 8) + \ - [60] * int(nRepeatTrials / 8) + [80] * int(nRepeatTrials / 8) - CSI[:] = [CSI[i] for i in order] - - # Set the task order - tasks = [[1, 0]] * (nTotalTrials + 1) - for i in list(range(nTotalTrials)): - if transitions[i] == 0: - tasks[i + 1] = tasks[i] - if transitions[i] == 1: - if tasks[i] == [1, 0]: - tasks[i + 1] = [0, 1] - if tasks[i] == [0, 1]: - tasks[i + 1] = [1, 0] - tasks = tasks[1:] - - # # Check whether combinations of transitions, stimuli and CSIs are counterbalanced - - # # This is used later to check whether trials are counterbalanced - # stimuli_type = [1] * int(nSwitchTrials/4) + [2] * int(nSwitchTrials/4) + [3] * int(nSwitchTrials/4) + [4] * int(nSwitchTrials/4) + \ - # [1] * int(nRepeatTrials/4) + [2] * int(nRepeatTrials/4) + [3] * int(nRepeatTrials/4) + [4] * int(nRepeatTrials/4) - # stimuli_type[:] = [stimuli_type[i] for i in order] - - # Trials = pd.DataFrame({'TrialType': transitions, - # 'Stimuli': stimuli_type, - # 'CSI': CSI - # }, columns= ['TrialType', 'Stimuli', 'CSI']) - # - # trial_counts = Trials.pivot_table(index=['TrialType', 'Stimuli', 'CSI'], aggfunc='size') - # print (trial_counts) - - return tasks, stimuli, CSI - - -# Stability-Flexibility Model - -GAIN = 1 -LEAK = 1 -COMP = 7.5 - -AUTOMATICITY = .15 # Automaticity Weight - -STARTING_POINT = 0.0 # Starting Point -THRESHOLD = 0.2 # Threshold -NOISE = 0.1 # Noise -SCALE = 1 # Scales DDM inputs so threshold can be set to 1 - -# Task Layer: [Color, Motion] {0, 1} Mutually Exclusive -# Origin Node -taskLayer = pnl.TransferMechanism(size=2, - function=pnl.Linear(slope=1, intercept=0), - output_ports=[pnl.RESULT], - name='Task Input [I1, I2]') - -# Stimulus Layer: [Color Stimulus, Motion Stimulus] -# Origin Node -stimulusInfo = pnl.TransferMechanism(size=2, - function=pnl.Linear(slope=1, intercept=0), - output_ports=[pnl.RESULT], - name="Stimulus Input [S1, S2]") - -# Cue-To-Stimulus Interval Layer -# Origin Node -# cueInterval = pnl.TransferMechanism(size=1, -# function=pnl.Linear(slope=1, intercept=0), -# output_ports=[pnl.RESULT], -# name='Cue-Stimulus Interval') - -# Control Module Layer: [Color Activation, Motion Activation] -controlModule = pnl.LCAMechanism(size=2, - function=pnl.Logistic(gain=GAIN), - leak=LEAK, - competition=COMP, - noise=0, - termination_measure=pnl.TimeScale.TRIAL, - termination_threshold=60, - time_step_size=.1, - name='Task Activations [Act1, Act2]') - -# Control Mechanism Setting Cue-To-Stimulus Interval -# csiController = pnl.ControlMechanism(monitor_for_control=cueInterval, -# control_signals=[(pnl.TERMINATION_THRESHOLD, controlModule)], -# modulation=pnl.OVERRIDE) - -# Hadamard product of controlModule and Stimulus Information -nonAutomaticComponent = pnl.TransferMechanism(size=2, - function=pnl.Linear(slope=1, intercept=0), - input_ports=pnl.InputPort(combine=pnl.PRODUCT), - output_ports=[pnl.RESULT], - name='Non-Automatic Component [S1*Act1, S2*Act2]') - -# Multiply Stimulus Input by the automaticity weight -congruenceWeighting = pnl.TransferMechanism(size=2, - function=pnl.Linear(slope=AUTOMATICITY, intercept=0), - output_ports=[pnl.RESULT], - name="Automaticity-weighted Stimulus Input [w*S1, w*S2]") - -# Summation of nonAutomatic and Automatic Components -ddmCombination = pnl.TransferMechanism(size=1, - function=pnl.Linear(slope=1, intercept=0), - input_ports=pnl.InputPort(combine=pnl.SUM), - output_ports=[pnl.RESULT], - name="Drift = (w*S1 + w*S2) + (S1*Act1 + S2*Act2)") - -# Scale DDM inputs to smaller value -ddmInputScale = pnl.TransferMechanism(size=1, - function=pnl.Linear(slope=SCALE, intercept=0), - output_ports=[pnl.RESULT], - name='Scaled DDM Input') - -# Decision Module -decisionMaker = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_point=STARTING_POINT, - threshold=THRESHOLD, - noise=NOISE, - time_step_size=0.001), - reset_stateful_function_when=pnl.AtTrialStart(), - output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], - name='DDM') - -taskLayer.set_log_conditions([pnl.RESULT]) -stimulusInfo.set_log_conditions([pnl.RESULT]) -#controlModule.set_log_conditions([pnl.RESULT, 'termination_threshold']) -controlModule.set_log_conditions([pnl.RESULT]) -nonAutomaticComponent.set_log_conditions([pnl.RESULT]) -congruenceWeighting.set_log_conditions([pnl.RESULT]) -ddmCombination.set_log_conditions([pnl.RESULT]) -ddmInputScale.set_log_conditions([pnl.RESULT]) -decisionMaker.set_log_conditions([pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME]) - -# Composition Creation -stabilityFlexibility = pnl.Composition(controller_mode=pnl.BEFORE) - -# Node Creation -stabilityFlexibility.add_node(taskLayer) -stabilityFlexibility.add_node(stimulusInfo) -# stabilityFlexibility.add_node(cueInterval) -stabilityFlexibility.add_node(controlModule) -# stabilityFlexibility.add_node(csiController) -stabilityFlexibility.add_node(nonAutomaticComponent) -stabilityFlexibility.add_node(congruenceWeighting) -stabilityFlexibility.add_node(ddmCombination) -stabilityFlexibility.add_node(ddmInputScale) -stabilityFlexibility.add_node(decisionMaker) - -# Projection Creation -stabilityFlexibility.add_projection(sender=taskLayer, receiver=controlModule) -stabilityFlexibility.add_projection(sender=controlModule, receiver=nonAutomaticComponent) -stabilityFlexibility.add_projection(sender=stimulusInfo, receiver=nonAutomaticComponent) -stabilityFlexibility.add_projection(sender=stimulusInfo, receiver=congruenceWeighting) -stabilityFlexibility.add_projection(sender=nonAutomaticComponent, receiver=ddmCombination) -stabilityFlexibility.add_projection(sender=congruenceWeighting, receiver=ddmCombination) -stabilityFlexibility.add_projection(sender=ddmCombination, receiver=ddmInputScale) -stabilityFlexibility.add_projection(sender=ddmInputScale, receiver=decisionMaker) - - - -# Hot-fix currently necessary to allow control module and DDM to execute in parallel in compiled mode -# We need two gates in order to output both values (decision and response) from the ddm -decisionGate = pnl.ProcessingMechanism(size=1, name="DECISION_GATE") -stabilityFlexibility.add_node(decisionGate) - -responseGate = pnl.ProcessingMechanism(size=1, name="RESPONSE_GATE") -stabilityFlexibility.add_node(responseGate) - -stabilityFlexibility.add_projection(sender=decisionMaker.output_ports[0], receiver=decisionGate) -stabilityFlexibility.add_projection(sender=decisionMaker.output_ports[1], receiver=responseGate) - -# Sets scheduler conditions, so that the gates are not executed (and hence the composition doesn't finish) until decisionMaker is finished -stabilityFlexibility.scheduler.add_condition(decisionGate, pnl.WhenFinished(decisionMaker)) -stabilityFlexibility.scheduler.add_condition(responseGate, pnl.WhenFinished(decisionMaker)) - -# Origin Node Inputs -USE_GPU = True - -taskTrain, stimulusTrain, cueTrain = generateTrialSequence(80, 0.5) - -taskTrain = np.array([[1.0, 0.0], [0., 1.], [1., 0.]]) -stimulusTrain = np.array([[-1., 1.], [ 1., 1.], [-1., -1.]]) -cueTrain = np.array([60., 60., 80.]) - -# inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain, cueInterval: cueTrain} -inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain} - - -# stabilityFlexibility.show_graph() - -if not USE_GPU: - stabilityFlexibility.run(inputs, bin_execute=True) - - import time - t0 = time.time() - stabilityFlexibility.run(inputs, bin_execute="LLVMRun", ) - - res = stabilityFlexibility.results -else: - ocm_mode = "PTX" - - search_range = pnl.SampleSpec(start=1.0, stop=7.5, step=0.001) - control_signal = pnl.ControlSignal(projections=[('competition', controlModule)], - variable=1.0, - allocation_samples=search_range, - intensity_cost_function=pnl.Linear(slope=0.)) - - objective_mech = pnl.ObjectiveMechanism(monitor=[responseGate]) - ocm = pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, - features=[stimulusInfo.input_port], - objective_mechanism=objective_mech, - function=pnl.GridSearch(), - control_signals=[control_signal], - comp_execution_mode=ocm_mode) - # objective_mech.log.set_log_conditions(pnl.OUTCOME) - - stabilityFlexibility.add_controller(ocm) - - import time - - t0 = time.time() - stabilityFlexibility.run(inputs, bin_execute='Python-PTX') - print(f"Elapsed: {time.time() - t0}") - -# taskLayer.log.print_entries() -# stimulusInfo.log.print_entries() -# controlModule.log.print_entries() -# nonAutomaticComponent.log.print_entries() -# congruenceWeighting.log.print_entries() -# ddmCombination.log.print_entries() -# ddmInputScale.log.print_entries() -# decisionMaker.log.print_entries() - -# from matplotlib import pyplot as plt -# ddm_log = ddmInputScale.log.nparray_dictionary() -# lca_log = controlModule.log.nparray_dictionary() -# plt.plot(lca_log['Composition-0']['RESULT'][:,0], lw=2, color='orange') -# plt.plot(lca_log['Composition-0']['RESULT'][:,1], lw=2, color='red') -# plt.plot(ddm_log['Composition-0']['RESULT'], lw=2, color='green') -# -# for trial in stabilityFlexibility.results: -# print(trial) diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py index 77767eacd65..7ee1793f859 100644 --- a/psyneulink/core/components/functions/fitfunctions.py +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -6,6 +6,7 @@ from psyneulink.core.globals.parameters import Parameter from psyneulink.core.scheduling.condition import AtTrialStart from psyneulink.core.globals.parameters import Parameter +from psyneulink.core.llvm import ExecutionMode import typing @@ -342,7 +343,7 @@ def log_likelihood(**kwargs): composition.run( inputs=inputs, num_trials=num_sims_per_trial * num_trials, - bin_execute=True, + execution_mode=ExecutionMode.LLVMRun, context=context, ) diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index d805575bf4b..58daab4004d 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -765,17 +765,6 @@ def __init__(self, prefs: tc.optional(is_pref_set) = None, **kwargs): - # New (1/19/2021) default behavior of DDM mechanism is to reset stateful functions - # on each new trial. - self.reset_stateful_function_when = kwargs.get('reset_stateful_function_when', AtTrialStart()) - - # New (1/19/2021) default behaviour of DDM mechanism is to execute until finished. That - # is, it should execute until it reaches its threshold. - self.execute_until_finished = kwargs.get('execute_until_finished', True) - - # FIXME: Set maximum executions absurdly large to avoid early termination - self.max_executions_before_finished = sys.maxsize - # Override instantiation of StandardOutputPorts usually done in _instantiate_output_ports # in order to use SEQUENTIAL indices self.standard_output_ports = StandardOutputPorts(self, self.standard_output_ports, indices=SEQUENTIAL) @@ -859,7 +848,17 @@ def __init__(self, # # Conflict with above # self.size = size - self.parameters.execute_until_finished.default_value = False + # New (1/19/2021) default behaviour of DDM mechanism is to execute until finished. That + # is, it should execute until it reaches its threshold. + self.parameters.execute_until_finished.default_value = True + + # New (1/19/2021) default behavior of DDM mechanism is to reset stateful functions + # on each new trial. + if 'reset_stateful_function_when' not in kwargs: + kwargs['reset_stateful_function_when'] = AtTrialStart() + + # FIXME: Set maximum executions absurdly large to avoid early termination + self.max_executions_before_finished = sys.maxsize super(DDM, self).__init__(default_variable=default_variable, random_state=random_state, From edc613dcd02150813d971a2db5be5719c3c21591 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 4 Mar 2021 15:21:26 -0500 Subject: [PATCH 033/285] Pin fastkde, only version with working wheels right now --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3feaec289d4..fbc127e4b33 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,5 +14,5 @@ torch<1.8.0; sys_platform != 'win32' and platform_machine == 'x86_64' and platfo typecheck-decorator<=1.2 leabra-psyneulink<=0.3.2 pandas<=1.0.5 -psyneulink-fastkde>=1.0.19 +fastkde==1.0.19 rich>=9.11, <9.13 From b59885430d281dc5c9c90986ddaf64f0d30ff935 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 4 Mar 2021 15:28:35 -0500 Subject: [PATCH 034/285] fix: ignore JSON files created in tests/json/ Maybe these should be generated in tmpdir instead. I left as is for now since it isn't my test. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 654943bed71..3be5e7eb5f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ # Created by https://www.gitignore.io/api/osx,python,pycharm +# Ignore JSON files created in tests/json/ +# Maybe these should be generated in tmpdir instead +tests/json/*.json + # Log files created by SLURM jobs in this directory Scripts/Debug/predator_prey_opt/logs/ From 0f719d829aba1424c03e5d572545881a856fddb9 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 5 Mar 2021 11:46:26 -0500 Subject: [PATCH 035/285] Set two DDM tests in ExecutionMode.LLVM to xfail. See: https://github.com/PrincetonUniversity/PsyNeuLink/issues/1935 --- tests/mechanisms/test_ddm_mechanism.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/mechanisms/test_ddm_mechanism.py b/tests/mechanisms/test_ddm_mechanism.py index 90b055eee0e..b4d47c69adb 100644 --- a/tests/mechanisms/test_ddm_mechanism.py +++ b/tests/mechanisms/test_ddm_mechanism.py @@ -706,6 +706,15 @@ def test_DDM_threshold_modulation(comp_mode): (100.0, 100.0, (100.0, 76.0)), ]) def test_ddm_is_finished(comp_mode, noise, threshold, expected_results): + + # 3/5/2021 - DDM' default behaviour now requires resetting stateful + # functions after each trial. This is not supported in LLVM execution mode. + # See: https://github.com/PrincetonUniversity/PsyNeuLink/issues/1935 + if comp_mode == pnl.ExecutionMode.LLVM: + pytest.xfail(reason="DDM' default behaviour now requires resetting stateful functions after each trial. " + "This is not supported in LLVM execution mode. " + "See: https://github.com/PrincetonUniversity/PsyNeuLink/issues/1935") + comp = Composition() ddm = DDM(function=DriftDiffusionIntegrator(threshold=threshold, noise=np.sqrt(noise), time_step_size=1.0), execute_until_finished=True) @@ -776,6 +785,16 @@ def test_sequence_of_DDM_mechs_in_Composition_Pathway(): @pytest.mark.mechanism @pytest.mark.ddm_mechanism def test_DDMMechanism_LCA_equivalent(comp_mode): + + # 3/5/2021 - DDM' default behaviour now requires resetting stateful + # functions after each trial. This is not supported in LLVM execution mode. + # See: https://github.com/PrincetonUniversity/PsyNeuLink/issues/1935 + if comp_mode == pnl.ExecutionMode.LLVM: + pytest.xfail(reason="DDM' default behaviour now requires resetting stateful functions after each trial. " + "This is not supported in LLVM execution mode. " + "See: https://github.com/PrincetonUniversity/PsyNeuLink/issues/1935") + + ddm = DDM(default_variable=[0], function=DriftDiffusionIntegrator(rate=1, time_step_size=0.1), execute_until_finished=False) comp2 = Composition() From b0a74eb34aa7cf6896085841707c409380625144 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 16 Mar 2021 14:26:36 -0400 Subject: [PATCH 036/285] Cleanup PyTorch DMM example --- Scripts/Debug/ddm/pytorch_ddm.py | 162 ++++++++++-------- .../mechanisms/processing/integrator/ddm.py | 3 + 2 files changed, 92 insertions(+), 73 deletions(-) diff --git a/Scripts/Debug/ddm/pytorch_ddm.py b/Scripts/Debug/ddm/pytorch_ddm.py index 0581b3ad862..98d3ca6d92d 100644 --- a/Scripts/Debug/ddm/pytorch_ddm.py +++ b/Scripts/Debug/ddm/pytorch_ddm.py @@ -3,54 +3,61 @@ import torch from torch import nn +from typing import Tuple + if torch.cuda.is_available(): dev = "cuda:0" else: dev = "cpu" -def simulate_ddms(starting_value: float = 0.0, - rate: float = 1.0, - non_decision_time: float = 0.0, - threshold: float = 1.0, - noise: float = 1.0, - time_step_size: float = 0.01, - num_walkers: int = 1000, - dev: str = "cuda:0"): - """ - A simulation of - - Args: - starting_value: - rate: - non_decision_time: - threshold: - noise: - time_step_size: - num_walkers: - dev: - - Returns: - - """ - - particle = torch.full(size=(num_walkers,), fill_value=starting_value, device=dev) - active = torch.ones(size=(num_walkers,), dtype=torch.bool, device=dev) - rts = torch.zeros(size=(num_walkers,), device=dev) - - for i in range(3000): - #dw = torch.distributions.Normal(loc=rate * time_step_size * active, scale=noise * active).rsample() - dw = torch.normal(mean=rate * time_step_size * active, std=noise * active) - particle = particle + dw * torch.sqrt(time_step_size) - rts = rts + active - active = torch.abs(particle) < threshold - - rts = (non_decision_time + rts * time_step_size) - - decisions = torch.ones(size=(num_walkers,), device=dev) - decisions[particle <= -threshold] = 0 +class DriftDiffusionModel(torch.nn.Module): + def forward(self, + starting_value: float = 0.0, + rate: float = 1.0, + non_decision_time: float = 0.0, + threshold: float = 1.0, + noise: float = 1.0, + time_step_size: float = 0.01, + num_walkers: int = 1000, + dev: str = "cuda:0") -> Tuple[torch.Tensor, torch.Tensor]: + + """ + A model that simulates many instances of a simple noisy drift diffusion model in parallel. + + Args: + starting_value: The starting value of each particle in the model. + rate: The drift rate for each particle. + non_decision_time: A constant amount of time added to each reaction time that signifies automatic processing + times of stimuli. + threshold: The threshold that a particle must reach to stop integration. + noise: The standard deviation of the Gaussian noise added to each particles position at each time step. + time_step_size: The time step size (in seconds) for the integration process. + num_walkers: The number of particles to simulate. + dev: The device the model should be run on. + + Returns: + A two element tuple containing the reaction times and the decisions + """ + + particle = torch.ones(size=(num_walkers,), device=dev) * starting_value + active = torch.ones(size=(num_walkers,), dtype=torch.bool, device=dev) + rts = torch.zeros(size=(num_walkers,), device=dev) + + for i in range(3000): + #dw = torch.distributions.Normal(loc=rate * time_step_size * active, scale=noise * active).rsample() + dw = torch.normal(mean=rate * time_step_size * active, std=noise * active) + particle = particle + dw * torch.sqrt(time_step_size) + rts = rts + active + active = torch.abs(particle) < threshold + + rts = (non_decision_time + rts * time_step_size) + + decisions = torch.ones(size=(num_walkers,), device=dev) + decisions[particle <= -threshold] = 0 + + return rts, decisions - return rts, decisions ddm_params = dict(starting_value=0.0, rate=0.3, non_decision_time=0.15, threshold=0.6, noise=1.0, time_step_size=0.001) @@ -63,50 +70,59 @@ def simulate_ddms(starting_value: float = 0.0, #%% t0 = time.time() -rts, decision = simulate_ddms(**ddm_params, num_walkers=NUM_WALKERS, dev=dev) +ddm_model = DriftDiffusionModel() +rts, decision = ddm_model(**ddm_params, num_walkers=NUM_WALKERS, dev=dev) rts = rts.to("cpu") decision = decision.to("cpu") -print(f"PyTorch Elapsed: {time.time() - t0}") +print(f"PyTorch Elapsed: {1000.0 * (time.time() - t0)} milliseconds") #%% # JIT -jit_simulate_ddms = torch.jit.script(simulate_ddms) +jit_ddm_model = torch.jit.script(ddm_model) #%% -rts, decision = jit_simulate_ddms(**ddm_params, num_walkers=NUM_WALKERS) - -NUM_TIMES = 50 -t0 = time.time() -for i in range(NUM_TIMES): - rts, decision = jit_simulate_ddms(**ddm_params, num_walkers=NUM_WALKERS) - rts = rts.to("cpu") - decision = decision.to("cpu") -print(f"JIT Elapsed: {1000 * ((time.time() - t0) / NUM_TIMES)} milliseconds") +# rts, decision = jit_ddm_model(**ddm_params, num_walkers=NUM_WALKERS) +# +# NUM_TIMES = 50 +# t0 = time.time() +# for i in range(NUM_TIMES): +# rts, decision = jit_ddm_model(**ddm_params, num_walkers=NUM_WALKERS) +# rts = rts.to("cpu") +# decision = decision.to("cpu") +# print(f"JIT Elapsed: {1000 * ((time.time() - t0) / NUM_TIMES)} milliseconds") #%% -class DriftDiffusionModel(torch.nn.Module): - def forward(self, - starting_value, - rate, - non_decision_time, - threshold, - noise, - time_step_size, - num_walkers): - return simulate_ddms(starting_value, rate, non_decision_time, threshold, noise, time_step_size, num_walkers) + +jit_ddm_model.save("ddm.pt") #%% -ddm_model = torch.jit.script(DriftDiffusionModel()) +# with open('ddm.onnx', 'wb') as file: +# torch.onnx.export(model=torch.jit.script(DriftDiffusionModel()), +# args=tuple(ddm_params.values()) + (torch.tensor(NUM_WALKERS),), +# example_outputs=(rts, decision), +# f=file, +# verbose=True, +# opset_version=12) -with open('ddm.onnx', 'wb') as file: - torch.onnx.export(model=ddm_model, - args=tuple(ddm_params.values()) + (torch.tensor(NUM_WALKERS),), - example_outputs=(rts, decision), - f=file, - verbose=True, - opset_version=11) +#%% +# import seaborn as sns +# sns.kdeplot(rts) #%% -import seaborn as sns -sns.kdeplot(rts) +graph = jit_ddm_model.graph +parsed_graph = {} +graph_nodes = [] +for node in graph.nodes(): + op = node.kind(); + outputs = [o.unique() for o in node.outputs()] + inputs = [i.unique() for i in node.inputs()] + graph_node = # Create a node for current AST Node + parsed_graph[graph_node.id] = graph_node + # Reference: + # https://github.com/waleedka/hiddenlayer/blob/master/hiddenlayer/pytorch_builder.py + for to in graph.nodes(): + to_inputs = [i.unique() for i in to.inputs()] + edges = set(outputs) & set(to_inputs) + if edges: + graph_node.edges.append(Edge(to, edges)) \ No newline at end of file diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index 58daab4004d..b3796237774 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -845,6 +845,9 @@ def __init__(self, # set normally by default pass + if default_variable is None: + default_variable = np.array([0.0]) + # # Conflict with above # self.size = size From c27bdaaa889113626c92d75f7f95473bffa43d53 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 12 Apr 2021 12:39:25 -0400 Subject: [PATCH 037/285] Added a copy of Bryant's model --- Scripts/Debug/ddm/pytorch_ddm.py | 3 + .../stability_flexibility.py | 276 ++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 Scripts/Debug/stability_flexibility/stability_flexibility.py diff --git a/Scripts/Debug/ddm/pytorch_ddm.py b/Scripts/Debug/ddm/pytorch_ddm.py index 98d3ca6d92d..51ef471ca2c 100644 --- a/Scripts/Debug/ddm/pytorch_ddm.py +++ b/Scripts/Debug/ddm/pytorch_ddm.py @@ -111,6 +111,9 @@ def forward(self, #%% graph = jit_ddm_model.graph +nodes = list(graph.nodes()) + +#%% parsed_graph = {} graph_nodes = [] for node in graph.nodes(): diff --git a/Scripts/Debug/stability_flexibility/stability_flexibility.py b/Scripts/Debug/stability_flexibility/stability_flexibility.py new file mode 100644 index 00000000000..5bd95907384 --- /dev/null +++ b/Scripts/Debug/stability_flexibility/stability_flexibility.py @@ -0,0 +1,276 @@ +import psyneulink as pnl +import numpy as np +import random +import pytest +import pandas as pd + + +# Define function to generate a counterbalanced trial sequence with a specified switch trial frequency +def generateTrialSequence(N, Frequency): + # Compute trial number + nTotalTrials = N + switchFrequency = Frequency + + nSwitchTrials = int(nTotalTrials * switchFrequency) + nRepeatTrials = int(nTotalTrials - nSwitchTrials) + + # Determine task transitions + transitions = [1] * nSwitchTrials + [0] * nRepeatTrials + order = np.random.permutation(list(range(nTotalTrials))) + transitions[:] = [transitions[i] for i in order] + + # Determine stimuli with 50% congruent trials + stimuli = [[1, 1]] * int(nSwitchTrials / 4) + [[1, -1]] * int(nSwitchTrials / 4) + [[-1, -1]] * int( + nSwitchTrials / 4) + [[-1, 1]] * int(nSwitchTrials / 4) + \ + [[1, 1]] * int(nRepeatTrials / 4) + [[1, -1]] * int(nRepeatTrials / 4) + [[-1, -1]] * int( + nRepeatTrials / 4) + [[-1, 1]] * int(nRepeatTrials / 4) + stimuli[:] = [stimuli[i] for i in order] + + # stimuli[:] = [[1, 1]] * nTotalTrials + + # Determine cue-stimulus intervals + CSI = [1200] * int(nSwitchTrials / 8) + [1200] * int(nSwitchTrials / 8) + \ + [1200] * int(nSwitchTrials / 8) + [1200] * int(nSwitchTrials / 8) + \ + [1200] * int(nSwitchTrials / 8) + [1200] * int(nSwitchTrials / 8) + \ + [1200] * int(nSwitchTrials / 8) + [1200] * int(nSwitchTrials / 8) + \ + [1200] * int(nRepeatTrials / 8) + [1200] * int(nRepeatTrials / 8) + \ + [1200] * int(nRepeatTrials / 8) + [1200] * int(nRepeatTrials / 8) + \ + [1200] * int(nRepeatTrials / 8) + [1200] * int(nRepeatTrials / 8) + \ + [1200] * int(nRepeatTrials / 8) + [1200] * int(nRepeatTrials / 8) + CSI[:] = [CSI[i] for i in order] + + # Set the task order + tasks = [[1, 0]] * (nTotalTrials + 1) + for i in list(range(nTotalTrials)): + if transitions[i] == 0: + tasks[i + 1] = tasks[i] + if transitions[i] == 1: + if tasks[i] == [1, 0]: + tasks[i + 1] = [0, 1] + if tasks[i] == [0, 1]: + tasks[i + 1] = [1, 0] + tasks = tasks[1:] + + # Determine correct response based on stimulus and task input + correctResponse = np.sum(np.multiply(tasks, stimuli), axis=1) + + # # Check whether combinations of transitions, stimuli and CSIs are counterbalanced + + # # This is used later to check whether trials are counterbalanced + # stimuli_type = [1] * int(nSwitchTrials/4) + [2] * int(nSwitchTrials/4) + [3] * int(nSwitchTrials/4) + [4] * int(nSwitchTrials/4) + \ + # [1] * int(nRepeatTrials/4) + [2] * int(nRepeatTrials/4) + [3] * int(nRepeatTrials/4) + [4] * int(nRepeatTrials/4) + # stimuli_type[:] = [stimuli_type[i] for i in order] + + # Trials = pd.DataFrame({'TrialType': transitions, + # 'Stimuli': stimuli_type, + # 'CSI': CSI + # }, columns= ['TrialType', 'Stimuli', 'CSI']) + # + # trial_counts = Trials.pivot_table(index=['TrialType', 'Stimuli', 'CSI'], aggfunc='size') + # print (trial_counts) + + return tasks, stimuli, CSI, correctResponse + + +# Stability-Flexibility Model + +def run_stab_flex(taskTrain, stimulusTrain, cueTrain, + gain=1.0, leak=1.0, competition=7.5, + lca_time_step_size=0.1, + non_decision_time=0.0, + automaticity=.15, + starting_point=0.0, + threshold=0.2, + ddm_noise=0.1, + lca_noise=0.0, + short_csi=None, + delta_csi=None, + scale=1, + ddm_time_step_size=0.001, + rng_seed=None): + # If the user has specified a short_csi and delta_csi as parameters, modify cueTrain + # such that its min is replaced with short_csi and its max (short_csi + delta_csi) + if delta_csi and short_csi: + csi_params = np.zeros(cueTrain.shape) + csi_params[cueTrain == np.min(cueTrain)] = short_csi + csi_params[cueTrain == np.max(cueTrain)] = short_csi + delta_csi + + GAIN = gain + LEAK = leak + COMP = competition + AUTOMATICITY = automaticity # Automaticity Weight + + STARTING_POINT = starting_point # Starting Point + THRESHOLD = threshold # Threshold + NOISE = ddm_noise # Noise + SCALE = scale # Scales DDM inputs so threshold can be set to 1 + + # Task Layer: [Color, Motion] {0, 1} Mutually Exclusive + # Origin Node + taskLayer = pnl.TransferMechanism(size=2, + function=pnl.Linear(slope=1, intercept=0), + output_ports=[pnl.RESULT], + name='Task Input [I1, I2]') + + # Stimulus Layer: [Color Stimulus, Motion Stimulus] + # Origin Node + stimulusInfo = pnl.TransferMechanism(size=2, + function=pnl.Linear(slope=1, intercept=0), + output_ports=[pnl.RESULT], + name="Stimulus Input [S1, S2]") + + # Cue-To-Stimulus Interval Layer + # Origin Node + cueInterval = pnl.TransferMechanism(size=1, + function=pnl.Linear(slope=1, intercept=0), + output_ports=[pnl.RESULT], + name='Cue-Stimulus Interval') + + # Correct Response Info + # Origin Node + correctResponseInfo = pnl.TransferMechanism(size=1, + function=pnl.Linear(slope=1, intercept=0), + output_ports=[pnl.RESULT], + name='Correct Response Info') + + # Control Module Layer: [Color Activation, Motion Activation] + controlModule = pnl.LCAMechanism(size=2, + function=pnl.Logistic(gain=GAIN), + leak=LEAK, + competition=COMP, + self_excitation=0, + noise=0, + termination_measure=pnl.TimeScale.TRIAL, + termination_threshold=1200, + time_step_size=lca_time_step_size, + name='Task Activations [Act1, Act2]') + + # Control Mechanism Setting Cue-To-Stimulus Interval + csiController = pnl.ControlMechanism(monitor_for_control=cueInterval, + control_signals=[(pnl.TERMINATION_THRESHOLD, controlModule)], + modulation=pnl.OVERRIDE) + + # Hadamard product of controlModule and Stimulus Information + nonAutomaticComponent = pnl.TransferMechanism(size=2, + function=pnl.Linear(slope=1, intercept=0), + input_ports=pnl.InputPort(combine=pnl.PRODUCT), + output_ports=[pnl.RESULT], + name='Non-Automatic Component [S1*Act1, S2*Act2]') + + # Multiply Stimulus Input by the automaticity weight + congruenceWeighting = pnl.TransferMechanism(size=2, + function=pnl.Linear(slope=AUTOMATICITY, intercept=0), + output_ports=[pnl.RESULT], + name="Automaticity-weighted Stimulus Input [w*S1, w*S2]") + + # Summation of nonAutomatic and Automatic Components + ddmCombination = pnl.TransferMechanism(size=1, + function=pnl.Linear(slope=1, intercept=0), + input_ports=pnl.InputPort(combine=pnl.SUM), + output_ports=[pnl.RESULT], + name="Drift = (w*S1 + w*S2) + (S1*Act1 + S2*Act2)") + + # Ensure upper boundary of DDM is always correct response by multiplying DDM input by correctResponseInfo + ddmRecodeDrift = pnl.TransferMechanism(size=1, + function=pnl.Linear(slope=1, intercept=0), + input_ports=pnl.InputPort(combine=pnl.PRODUCT), + output_ports=[pnl.RESULT], + name='Recoded Drift = Drift * correctResponseInfo') + + # Scale DDM inputs + ddmInputScale = pnl.TransferMechanism(size=1, + function=pnl.Linear(slope=SCALE, intercept=0), + output_ports=[pnl.RESULT], + name='Scaled DDM Input') + + # Decision Module + decisionMaker = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_point=STARTING_POINT, + threshold=THRESHOLD, + noise=NOISE, + time_step_size=ddm_time_step_size), + reset_stateful_function_when=pnl.AtTrialStart(), + output_ports=[pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], + name='DDM') + + taskLayer.set_log_conditions([pnl.RESULT]) + stimulusInfo.set_log_conditions([pnl.RESULT]) + cueInterval.set_log_conditions([pnl.RESULT]) + correctResponseInfo.set_log_conditions([pnl.RESULT]) + controlModule.set_log_conditions([pnl.RESULT, 'termination_threshold']) + nonAutomaticComponent.set_log_conditions([pnl.RESULT]) + congruenceWeighting.set_log_conditions([pnl.RESULT]) + ddmCombination.set_log_conditions([pnl.RESULT]) + ddmRecodeDrift.set_log_conditions([pnl.RESULT]) + ddmInputScale.set_log_conditions([pnl.RESULT]) + decisionMaker.set_log_conditions([pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME]) + + # Composition Creation + stabilityFlexibility = pnl.Composition() + + # Node Creation + stabilityFlexibility.add_node(taskLayer) + stabilityFlexibility.add_node(stimulusInfo) + stabilityFlexibility.add_node(cueInterval) + stabilityFlexibility.add_node(correctResponseInfo) + stabilityFlexibility.add_node(controlModule) + stabilityFlexibility.add_node(csiController) + stabilityFlexibility.add_node(nonAutomaticComponent) + stabilityFlexibility.add_node(congruenceWeighting) + stabilityFlexibility.add_node(ddmCombination) + stabilityFlexibility.add_node(ddmRecodeDrift) + stabilityFlexibility.add_node(ddmInputScale) + stabilityFlexibility.add_node(decisionMaker) + + # Projection Creation + stabilityFlexibility.add_projection(sender=taskLayer, receiver=controlModule) + stabilityFlexibility.add_projection(sender=controlModule, receiver=nonAutomaticComponent) + stabilityFlexibility.add_projection(sender=stimulusInfo, receiver=nonAutomaticComponent) + stabilityFlexibility.add_projection(sender=stimulusInfo, receiver=congruenceWeighting) + stabilityFlexibility.add_projection(sender=nonAutomaticComponent, receiver=ddmCombination) + stabilityFlexibility.add_projection(sender=congruenceWeighting, receiver=ddmCombination) + stabilityFlexibility.add_projection(sender=ddmCombination, receiver=ddmRecodeDrift) + stabilityFlexibility.add_projection(sender=correctResponseInfo, receiver=ddmRecodeDrift) + stabilityFlexibility.add_projection(sender=ddmRecodeDrift, receiver=ddmInputScale) + stabilityFlexibility.add_projection(sender=ddmInputScale, receiver=decisionMaker) + + # Hot-fix currently necessary to allow control module and DDM to execute in parallel in compiled mode + # We need two gates in order to output both values (decision and response) from the ddm + decisionGate = pnl.ProcessingMechanism(size=1, name="DECISION_GATE") + stabilityFlexibility.add_node(decisionGate) + + responseGate = pnl.ProcessingMechanism(size=1, name="RESPONSE_GATE") + stabilityFlexibility.add_node(responseGate) + + stabilityFlexibility.add_projection(sender=decisionMaker.output_ports[0], receiver=decisionGate) + stabilityFlexibility.add_projection(sender=decisionMaker.output_ports[1], receiver=responseGate) + + # Sets scheduler conditions, so that the gates are not executed (and hence the composition doesn't finish) until decisionMaker is finished + stabilityFlexibility.scheduler.add_condition(decisionGate, pnl.WhenFinished(decisionMaker)) + stabilityFlexibility.scheduler.add_condition(responseGate, pnl.WhenFinished(decisionMaker)) + + inputs = {taskLayer: taskTrain, + stimulusInfo: stimulusTrain, + cueInterval: cueTrain} + + stabilityFlexibility.run(inputs, bin_execute=False) + + return stabilityFlexibility + + +tasks, stimuli, CSI, correctResponse = generateTrialSequence(256, 0.5) + +comp = run_stab_flex(taskTrain=tasks, stimulusTrain=stimuli, CSI=CSI) + + + +# taskLayer.log.print_entries() +# stimulusInfo.log.print_entries() +# cueInterval.log.print_entries() +# correctResponseInfo.log.print_entries() +# controlModule.log.print_entries() +# nonAutomaticComponent.log.print_entries() +# congruenceWeighting.log.print_entries() +# ddmCombination.log.print_entries() +# ddmRecodeDrift.log.print_entries() +# ddmInputScale.log.print_entries() +# decisionMaker.log.print_entries() From c5c143f8b18581f58442d448b069fcd126167a81 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 19 Apr 2021 16:31:09 -0400 Subject: [PATCH 038/285] Added a fix for error generating LCAError LCAError exception being created when threshold and termination_threshold are both specified was tryng to use self.__name__ that does not exist. Changed it to self.__class__. Also added a PyTorch implemtation of LCA and stability flexibility model for benchmarking in the future. --- Scripts/Debug/ddm/pytorch_ddm.py | 21 -- .../stability_flexibility/pytorch_lca.py | 303 ++++++++++++++++++ .../pytorch_stability_flexibility.py | 133 ++++++++ .../stability_flexibility.py | 8 +- .../processing/transfer/lcamechanism.py | 2 +- 5 files changed, 441 insertions(+), 26 deletions(-) create mode 100644 Scripts/Debug/stability_flexibility/pytorch_lca.py create mode 100644 Scripts/Debug/stability_flexibility/pytorch_stability_flexibility.py diff --git a/Scripts/Debug/ddm/pytorch_ddm.py b/Scripts/Debug/ddm/pytorch_ddm.py index 51ef471ca2c..786416f6251 100644 --- a/Scripts/Debug/ddm/pytorch_ddm.py +++ b/Scripts/Debug/ddm/pytorch_ddm.py @@ -108,24 +108,3 @@ def forward(self, #%% # import seaborn as sns # sns.kdeplot(rts) - -#%% -graph = jit_ddm_model.graph -nodes = list(graph.nodes()) - -#%% -parsed_graph = {} -graph_nodes = [] -for node in graph.nodes(): - op = node.kind(); - outputs = [o.unique() for o in node.outputs()] - inputs = [i.unique() for i in node.inputs()] - graph_node = # Create a node for current AST Node - parsed_graph[graph_node.id] = graph_node - # Reference: - # https://github.com/waleedka/hiddenlayer/blob/master/hiddenlayer/pytorch_builder.py - for to in graph.nodes(): - to_inputs = [i.unique() for i in to.inputs()] - edges = set(outputs) & set(to_inputs) - if edges: - graph_node.edges.append(Edge(to, edges)) \ No newline at end of file diff --git a/Scripts/Debug/stability_flexibility/pytorch_lca.py b/Scripts/Debug/stability_flexibility/pytorch_lca.py new file mode 100644 index 00000000000..e9324755ac3 --- /dev/null +++ b/Scripts/Debug/stability_flexibility/pytorch_lca.py @@ -0,0 +1,303 @@ +import torch +import numpy as np +import time + +import matplotlib.pyplot as plt +import seaborn as sns +import pandas as pd + +from typing import Union, Iterable, Tuple, Optional, Callable + +if torch.cuda.is_available(): + dev = "cuda:0" +else: + dev = "cpu" + + +class LCALayer(torch.nn.Module): + def __init__(self, + threshold: Union[float, None] = 1.0, + leak: float = 0.1, + competition: float = 0.1, + self_excitation: float = 0.0, + non_decision_time: float = 0.0, + activation_function: Callable = torch.relu, + noise: Union[float, torch.Tensor, None] = 1.0, + time_step_size: float = 0.01): + """ + + Args: + threshold: The threshold that accumulators must reach to stop integration. If None, accumulators will + never stop integrating even when the pass the threshold. + leak: The decay rate, which reflects leakage of the activation. + competition: The weight to apply for inhibitory influence from other activations. Positive values lead + to inhibitory effects from other accumulators. + self_excitation: The weight to apply for the scaling factor for the recurrent excitation + non_decision_time: The time that should be added to reaction times to account for stimulus encoding and + response generation. This parameter shifts the results reactions times by and offset. This parameter + is not actually used by LCALayer since the forward method only computes a timestep at a time. However, + this is a common LCA model parameter so it should be stored with the others. It is added to final + reaction time generated from LCAModel.forward. + activation_function: The non-linear function to apply to pre activities to get activities. This is + torch.relu by default, but can be any callable. + noise: The standard deviation of the Gaussian noise added to each particles position at each time step. + time_step_size: The time step size (in seconds) for the integration process. + dev: Device to run compute on. + + """ + super(LCALayer, self).__init__() + + require_grad = False + + self.leak = leak + self.competition = competition + self.self_excitation = self_excitation + self.noise = noise + self.time_step_size = time_step_size + self.non_decision_time = non_decision_time + self._sqrt_step_size = torch.sqrt(torch.tensor(0.001, requires_grad=require_grad).to(dev)) + self.threshold = threshold + self.activation_function = activation_function + + def forward(self, + input: torch.Tensor, + pre_activities: torch.Tensor, + activities: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """ + Compute one time step of integration for a batch of independent leaky competing accumulator + instances. + + Args: + input: The current input activity for the accumulators + pre_activities: The activity of the accumulator without the application of the non-linearity + activation_func. A 2D tensors of shape [batch_size, num_accumulators]. batch_size in + this case is each independent accumulator model instance. Or in other words, a simulation of the model. + activities: The current activity of each accumulator instance. This is the value after + the non-linearity activation_func has been applied to pre_activities. A 2D tensors of shape + [batch_size, num_accumulators]. batch_size in this case is each independent accumulator + model instance. Or in other words, a simulation of the model. + + Returns: + The current output activities of the accumulators, of same shape as activities. + """ + dev = "cuda:0" + num_simulations, num_lca_dim = activities.shape + + # Mark all accumulators as active by default + active = torch.ones(size=(num_simulations, 1), device=dev) + + # If threshold is provided, only integrate accumulators until they reach the threshold. + if self.threshold is not None: + active = torch.all(torch.abs(activities) < self.threshold, dim=1, keepdim=True) + + # Construct a gamma matrix, this is multiplied by each accumulator vector to compute + # the competitive inhibition and self excitation between units. + gamma = (torch.eye(num_lca_dim, device=dev) == 0.0) * -self.competition + gamma.fill_diagonal_(self.self_excitation) + + # Perform one time step of the integration + pre_activities = pre_activities + (input - self.leak * pre_activities + + torch.sum(torch.matmul(activities, gamma), + dim=1, keepdim=True)) * active * self.time_step_size + + # If standard deviation of noise is provided. Generate a noise for each accumulator. + # Only active accumulators will get noise + if self.noise is not None: + dw = torch.normal(mean=torch.zeros(activities.size(), device=dev), + std=active * self.noise * torch.ones(activities.size(), device=dev)) + pre_activities = pre_activities + dw * self._sqrt_step_size + + # Calculate the post activation function activities. Don't overwrite pre_activities, we will need these for + # the next timestep. + activities = self.activation_function(pre_activities) + + return pre_activities, activities, active + + +class LCAModel(torch.nn.Module): + def __init__(self, + lca_layer: LCALayer, + num_lca_dim: int, + num_simulations: int = 10000, + num_time_steps: int = 3000, + save_activities: bool = False): + """ + A model that simulates a leaky competing accumulator model (Usher and McClelland). + + References: + + Usher M, McClelland JL. The time course of perceptual choice: the leaky, competing accumulator model. + Psychol Rev. 2001 Jul;108(3):550-92. doi: 10.1037/0033-295x.108.3.550. PMID: 11488378. + + Args: + lca_layer: The LCALayer that computes the integration for each timestep. + num_lca_dim: The number of LCA units or accumulators. + num_simulations: The number of parallel independent simulations to run. + num_time_steps: The total number of time steps to run. + save_activities: Should activities be saved. If True, make sure the number of simulations is low. + + """ + + super(LCAModel, self).__init__() + + self.lca_layer = lca_layer + self.num_lca_dim = num_lca_dim + self.num_simulations = num_simulations + self.num_time_steps = num_time_steps + self.save_activities = save_activities + + def forward(self, input: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + """ + + Args: + input: + + Returns: + + """ + dev = "cuda:0" + + # Mark all accumulators as active by default + active = torch.ones(size=(self.num_simulations, 1), device=dev) + + if self.save_activities: + pre_activities = torch.zeros(size=(self.num_simulations, + self.num_lca_dim, + self.num_time_steps + 1), device=dev) + activities = torch.zeros(size=(self.num_simulations, + self.num_lca_dim, + self.num_time_steps + 1), device=dev) + else: + pre_activities = torch.zeros(size=(self.num_simulations, self.num_lca_dim), device=dev) + activities = torch.zeros(size=(self.num_simulations, self.num_lca_dim), device=dev) + + rts = torch.zeros(size=(self.num_simulations, 1), device=dev) + + # Simulate N time steps of the model. + for time_i in range(self.num_time_steps): + + # Compute the LCA activities + if self.save_activities: + pre_activities[:, :, time_i+1], activities[:, :, time_i+1], active = \ + self.lca_layer(input=input, + pre_activities=pre_activities[:, :, time_i], + activities=activities[:, :, time_i]) + else: + pre_activities, activities, active = self.lca_layer(input=input, pre_activities=pre_activities, + activities=activities) + + # Only increment reaction time for active simulations + rts = rts + active + + # Convert the time step index to actual time + rts = rts * self.lca_layer.time_step_size + + # Include the non-decision time in the reaction time. + rts = rts + self.lca_layer.non_decision_time + + # Figure out which accumulator crossed the threshold, this is the decision + torch.max(activities, dim=1) + + if not self.save_activities: + max_values, decisions = torch.max(activities, dim=1, keepdim=True) + + # Find any simulations that had multiple accumulators cross the threshold at the same time. + # Exclude these for simplicity. + good = torch.logical_and(torch.sum(activities == max_values, dim=1) == 1, ~torch.squeeze(active)) + decisions = decisions[good] + rts = rts[good] + + else: + decisions = torch.argmax(activities[:, :, self.num_time_steps], dim=1) + + if self.save_activities: + return pre_activities, activities + else: + return rts, decisions + + +if __name__ == "__main__": + + save_activities = False + + if save_activities: + num_simulations = 100 + else: + num_simulations = 50000 + + lca = LCAModel(lca_layer=LCALayer( + threshold=0.16, + leak=1.22, + competition=5.53, + self_excitation=0.0, + non_decision_time=0.45, + activation_function=torch.relu, + noise=np.sqrt(0.1), + time_step_size=0.001), + num_lca_dim=2, + num_simulations=num_simulations, + num_time_steps=3000, + save_activities=save_activities + ) + + lca = torch.jit.script(lca) + + # Create some input for the LCA, typically this would be generated from another part of the model + # that processes stimulus or tasks information into an N sized vector, where N is the number of + # dimensions in the LCA accumulator. + input = torch.Tensor([1.02, 1.02+0.25]).to(dev) + + # rts, decisions = lca(input) + # average_rt = rts.mean() + # average_rt.backward() + + # Warm things up before running things for timing + lca(input) + lca(input) + + t0 = time.time() + + for i in range(10): + # Run many independent simulations of the same LCA model + if save_activities: + pre_activities, activities = lca(input=input) + activities = activities.to("cpu").numpy() + pre_activities = pre_activities.to("cpu").numpy() + else: + rts, decisions = lca(input=input) + rts = rts.to("cpu").numpy().flatten() + decisions = decisions.to("cpu").numpy().flatten() + + t1 = time.time() + print(f"Average Elapsed: {(t1-t0) / 10.0} seconds") + + if save_activities: + pre_activities = np.transpose(pre_activities[0, :, :]) + activities = np.transpose(activities[0, :, :]) + preact_df = pd.DataFrame(pre_activities, columns=[f"{i}" for i in range(pre_activities.shape[1])]) + preact_df['name'] = 'x' + preact_df['t'] = np.arange(3001) * lca.lca_layer.time_step_size + act_df = pd.DataFrame(activities, columns=[f"{i}" for i in range(activities.shape[1])]) + act_df['name'] = 'f(x)' + act_df['t'] = np.arange(3001) * lca.lca_layer.time_step_size + results = pd.concat([preact_df, act_df]) + results = pd.melt(results, id_vars=['name', 't'], var_name='lca_unit') + + g = sns.FacetGrid(results, col="name", hue="lca_unit") + g.map_dataframe(sns.lineplot, x="t", y="value") + g.set_axis_labels("Time (s)", "Activity") + g.add_legend() + + ax1, ax2 = g.axes[0] + + ax1.axhline(lca.lca_layer.threshold, ls='--') + ax2.axhline(lca.lca_layer.threshold, ls='--') + + plt.show() + + else: + results = pd.DataFrame({'reaction_time': rts, 'decision': decisions}) + sns.kdeplot(data=results, x='reaction_time', hue='decision') + plt.xlim([0.0, 3.5]) + plt.show() + diff --git a/Scripts/Debug/stability_flexibility/pytorch_stability_flexibility.py b/Scripts/Debug/stability_flexibility/pytorch_stability_flexibility.py new file mode 100644 index 00000000000..83b11ef75fd --- /dev/null +++ b/Scripts/Debug/stability_flexibility/pytorch_stability_flexibility.py @@ -0,0 +1,133 @@ +from typing import Union, Iterable, Tuple, Optional, Callable + +import numpy as np +import torch + +from pytorch_lca import LCALayer + +if torch.cuda.is_available(): + dev = "cuda:0" +else: + dev = "cpu" + + +class DriftDiffusionModel(torch.nn.Module): + def forward(self, + activities: torch.Tensor, + rate: float = 1.0, + threshold: float = 1.0, + noise: float = 1.0, + time_step_size: float = 0.01) -> Tuple[torch.Tensor, torch.Tensor]: + + """ + A model that simulates many instances of a simple noisy drift diffusion model in parallel. + + Args: + activities: The current actitivies of the DDM + rate: The drift rate for each particle. + threshold: The threshold that a particle must reach to stop integration. + noise: The standard deviation of the Gaussian noise added to each particles position at each time step. + time_step_size: The time step size (in seconds) for the integration process. + + Returns: + A two element tuple containing the reaction times and the decisions + """ + + if threshold is not None: + active = torch.abs(activities) < threshold + else: + active = torch.ones(size=activities.size(), dev=dev) + + dw = torch.normal(mean=rate * time_step_size * active, std=noise * active) + activities = activities + dw * np.sqrt(time_step_size) + + return activities, active + + +# Model Parameters +GAIN = 1.0 +LEAK = 1.0 +COMP = 7.5 +AUTOMATICITY = 0.15 # Automaticity Weight +STARTING_VALUE = 0.0 # Starting Point +THRESHOLD = 0.5 # Threshold +LCA_NOISE = None # Noise +DDM_NOISE = 0.1 +SCALE = 1.0 # Scales DDM inputs so threshold can be set to 1 +NON_DECISION_TIME = 0.1 + +# Number of simulations to run. Each simulation is independent. +NUM_SIMULATIONS = 1000 + +# Run the model +N_TIME_STEPS = 3000 +TIME_STEP_SIZE = 0.01 + +stimuli = np.array([[-1, -1], + [-1, -1], + [1, 1], + [-1, 1], + [-1, -1], + [-1, 1]]) + +tasks = np.array([[0, 1], + [1, 0], + [1, 0], + [1, 0], + [1, 0], + [0, 1]]) + +lca = LCALayer() +ddm = DriftDiffusionModel() + +cue_stimulus_intervals = np.array([[0.3], [0.3], [0.3], [0.3], [0.3], [0.3]]) + +NUM_TRIALS = len(stimuli) + +# Initialize the LCA task activities, these are maintained throughout the whole +# experiment. +lca_activities = torch.zeros(size=(NUM_SIMULATIONS, 2), device=dev) + +for trial_idx in range(NUM_TRIALS): + + # Reset DDM activities for this trial. + ddm_activities = torch.ones(size=(NUM_SIMULATIONS,), device=dev) * STARTING_VALUE + rts = torch.zeros(size=(NUM_SIMULATIONS,), device=dev) + + stimulus = torch.from_numpy(stimuli[trial_idx]).float().to(dev) + task = torch.from_numpy(tasks[trial_idx]).float().to(dev) + csi = torch.from_numpy(cue_stimulus_intervals[trial_idx]).float().to(dev) + + # Compute the Automaticity-weighted Stimulus Input + auto_weight_stim = torch.sum(stimulus * AUTOMATICITY) + + # Simulate N time steps of the model for this trial + for time_i in range(N_TIME_STEPS): + + # Compute the LCA task activities + lca_activities, _ = lca(input=task, + activities=lca_activities, + threshold=None, + leak=LEAK, + competition=COMP, + self_excitation=0.0, + noise=None, + time_step_size=TIME_STEP_SIZE) + + # If the Cue Stimulus Interval time has passed, start the decision process. + if time_i * TIME_STEP_SIZE > csi: + # Compute the drift rate for the DDM from the task activations and the stimulus + non_automatic_component = torch.sum(lca_activities * stimulus, dim=1) + drift_rate = non_automatic_component + auto_weight_stim + + ddm_activities, ddm_active = ddm(activities=ddm_activities, rate=drift_rate, threshold=THRESHOLD, + noise=DDM_NOISE, time_step_size=TIME_STEP_SIZE) + + # Compute the reaction time for each simulation. + rts = rts + ddm_active + + # Compute reaction times in seconds for these trials + rts = (NON_DECISION_TIME + rts * TIME_STEP_SIZE) + + decisions = torch.ones(size=(NUM_SIMULATIONS,), device=dev) + decisions[ddm_activities <= -THRESHOLD] = 0 diff --git a/Scripts/Debug/stability_flexibility/stability_flexibility.py b/Scripts/Debug/stability_flexibility/stability_flexibility.py index 5bd95907384..8c0b54f0be8 100644 --- a/Scripts/Debug/stability_flexibility/stability_flexibility.py +++ b/Scripts/Debug/stability_flexibility/stability_flexibility.py @@ -184,7 +184,7 @@ def run_stab_flex(taskTrain, stimulusTrain, cueTrain, name='Scaled DDM Input') # Decision Module - decisionMaker = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_point=STARTING_POINT, + decisionMaker = pnl.DDM(function=pnl.DriftDiffusionIntegrator(starting_value=STARTING_POINT, threshold=THRESHOLD, noise=NOISE, time_step_size=ddm_time_step_size), @@ -252,16 +252,16 @@ def run_stab_flex(taskTrain, stimulusTrain, cueTrain, stimulusInfo: stimulusTrain, cueInterval: cueTrain} - stabilityFlexibility.run(inputs, bin_execute=False) + stabilityFlexibility.run(inputs, execution_mode=pnl.ExecutionMode.LLVMRun) return stabilityFlexibility tasks, stimuli, CSI, correctResponse = generateTrialSequence(256, 0.5) -comp = run_stab_flex(taskTrain=tasks, stimulusTrain=stimuli, CSI=CSI) - +comp = run_stab_flex(taskTrain=tasks, stimulusTrain=stimuli, cueTrain=CSI) +#comp.show_graph() # taskLayer.log.print_entries() # stimulusInfo.log.print_entries() diff --git a/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py index 363eb89a7b0..3b865a5c99e 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py @@ -559,7 +559,7 @@ def _parse_threshold_args(self, kwargs): if threshold is not None: if termination_threshold is not None: raise LCAError(f"The {repr('threshold')} and {repr(TERMINATION_THRESHOLD)} " - f"args of {self.__name__} can't both be specified.") + f"args of {self.__class__} can't both be specified.") else: termination_threshold = threshold else: From 064d63f3caa382730a896a672ad00482bd853ee3 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Thu, 29 Apr 2021 21:07:59 -0400 Subject: [PATCH 039/285] =?UTF-8?q?=E2=80=A2=20component.py=20=20=20docstr?= =?UTF-8?q?ing=20mod=20to=20**size**?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- psyneulink/core/components/component.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 8471a552f17..bfc64d65ae8 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -81,8 +81,9 @@ *Core Structural Attributes* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Every Component has the following set of core structural attributes. These attributes are not meant to be changed by the -user once the component is constructed, with the one exception of `prefs `. +Every Component has the following set of core structural attributes that can be specified in its constructor using the +arguments listed below. These attributes are not meant to be changed by the user once the component is constructed, +with the one exception of `prefs `. .. _Component_Type: @@ -96,7 +97,7 @@ used when the Component is executed and no input is provided), and takes precedence over the specification of `size `. - .. note:: + .. technical_note:: Internally, the attribute **variable** is not directly used as input to functions, to allow for parallelization. The attribute is maintained as a way for the user to monitor variable along the execution chain. During parallelization however, the attribute may not accurately represent the most current value of variable @@ -110,6 +111,12 @@ setting **size** = 3 is equivalent to setting **variable** = [0, 0, 0] and setting **size** = [4, 3] is equivalent to setting **variable** = [[0, 0, 0, 0], [0, 0, 0]]. + .. note:: + The size attribute serves a role similar to + `shape in Numpy`_, with the difference that + size permits the specification of `ragged arrays `_ -- that is, ones + that have elements of varying lengths, such as [[1,2],[3,4,5]]. + .. _Component_Function: * **function** - determines the computation that a Component carries out. It is always a PsyNeuLink `Function From 8cbc39fdcf009fa565921052cbef0400b1d4aebc Mon Sep 17 00:00:00 2001 From: davidt0x Date: Fri, 5 Nov 2021 13:42:25 -0400 Subject: [PATCH 040/285] WIP for pytorch and onnx LCA --- Scripts/Debug/lca/onnx_lca.py | 277 +++++++++++++++++++ Scripts/Debug/lca/pytorch_lca.py | 447 +++++++++++++++++++++++++++++++ 2 files changed, 724 insertions(+) create mode 100644 Scripts/Debug/lca/onnx_lca.py create mode 100644 Scripts/Debug/lca/pytorch_lca.py diff --git a/Scripts/Debug/lca/onnx_lca.py b/Scripts/Debug/lca/onnx_lca.py new file mode 100644 index 00000000000..3ccab45bbd7 --- /dev/null +++ b/Scripts/Debug/lca/onnx_lca.py @@ -0,0 +1,277 @@ +import torch +import onnx +import onnxruntime as ort + +from typing import Union, Iterable, Tuple, Optional, Callable, Any + + +if torch.cuda.is_available(): + def_dev = torch.device("cuda:0") +else: + def_dev = torch.device("cpu") + + +def scalar(val: torch.Tensor): + """Helper function to get scalars as Python numbers""" + return val.item() + + +class LCALayer(torch.nn.Module): + def __init__( + self, + threshold: Union[float, torch.FloatTensor, None] = torch.tensor(1.0), + leak: Union[torch.FloatTensor, float] = torch.tensor(0.1), + competition: Union[torch.FloatTensor, float] = torch.tensor(0.1), + self_excitation: Union[torch.FloatTensor, float] = torch.tensor(0.0), + non_decision_time: Union[torch.FloatTensor, float] = torch.tensor(0.0), + activation_function: Callable = torch.relu, + noise: Union[float, torch.Tensor, None] = torch.tensor(1.0), + time_step_size: Union[torch.FloatTensor, float] = torch.tensor(0.01), + ): + """ + An implementation of a Leaky Competing Accumulator as a layer. Each call to forward of this module only + implements one time step of the integration. See module LCAModel if you want to simulate an LCA to completion. + + Args: + threshold: The threshold that accumulators must reach to stop integration. If None, accumulators will + never stop integrating even when the pass the threshold. + leak: The decay rate, which reflects leakage of the activation. + competition: The weight to apply for inhibitory influence from other activations. Positive values lead + to inhibitory effects from other accumulators. + self_excitation: The weight to apply for the scaling factor for the recurrent excitation + non_decision_time: The time that should be added to reaction times to account for stimulus encoding and + response generation. This parameter shifts the results reactions times by and offset. This parameter + is not actually used by LCALayer since the forward method only computes a timestep at a time. However, + this is a common LCA model parameter so it should be stored with the others. It is added to final + reaction time generated from LCAModel.forward. + activation_function: The non-linear function to apply to pre activities to get activities. This is + torch.relu by default, but can be any callable. + noise: The standard deviation of the Gaussian noise added to each particles position at each time step. + time_step_size: The time step size (in seconds) for the integration process. + + """ + super().__init__() + + require_grad = False + + self.leak = leak + self.competition = competition + self.self_excitation = self_excitation + self.noise = noise + self.time_step_size = time_step_size + self.non_decision_time = non_decision_time + self._sqrt_step_size = torch.sqrt( + torch.tensor(0.001, requires_grad=require_grad).to(leak.device) + ) + self.threshold = threshold + self.activation_function = activation_function + + def forward( + self, + ff_input: torch.Tensor, + pre_activities: torch.Tensor, + activities: torch.Tensor, + ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """ + Compute one time step of integration for a batch of independent leaky competing accumulator + instances. + + Args: + ff_input: The current input activity for the accumulators + pre_activities: The activity of the accumulator without the application of the non-linearity + activation_func. A 2D tensors of shape [batch_size, num_accumulators]. batch_size in + this case is each independent accumulator model instance. Or in other words, a simulation of the model. + activities: The current activity of each accumulator instance. This is the value after + the non-linearity activation_func has been applied to pre_activities. A 2D tensors of shape + [batch_size, num_accumulators]. batch_size in this case is each independent accumulator + model instance. Or in other words, a simulation of the model. + + Returns: + The current output activities of the accumulators, of same shape as activities. + """ + num_simulations, num_lca_dim = activities.shape + + dev = self.leak.device + + # Mark all accumulators as active by default + active = torch.ones(size=(num_simulations, 1), device=dev) + + # If threshold is provided, only integrate accumulators until they reach the threshold. + # if self.threshold is not None: + # active = torch.all( + # torch.abs(activities) < self.threshold, dim=1, keepdim=True + # ) + + # Construct a gamma matrix, this is multiplied by each accumulator vector to compute + # the competitive inhibition and self excitation between units. + gamma = (torch.eye(num_lca_dim, device=dev) == 0.0) * self.competition + gamma.fill_diagonal_(-self.self_excitation) + + # Perform one time step of the integration + pre_activities = ( + pre_activities + + (ff_input - self.leak * pre_activities - torch.matmul(activities, gamma)) + * active + * self.time_step_size + ) + + # If standard deviation of noise is provided. Generate a noise for each accumulator. + # Only active accumulators will get noise + if self.noise is not None: + dw = torch.normal( + mean=torch.zeros(activities.size(), device=dev), + std=active * self.noise * torch.ones(activities.size(), device=dev), + ) + pre_activities = pre_activities + dw * self._sqrt_step_size + + # Calculate the post activation function activities. Don't overwrite pre_activities, we will need these for + # the next timestep. + activities = self.activation_function(pre_activities) + + return pre_activities, activities, active + + +class LCAModel(torch.nn.Module): + def __init__( + self, + lca_layer: LCALayer, + num_lca_dim: int, + num_simulations: int = 10000, + num_time_steps: int = 3000, + ): + """ + A model that simulates a leaky competing accumulator model (Usher and McClelland). + + References: + + Usher M, McClelland JL. The time course of perceptual choice: the leaky, competing accumulator model. + Psychol Rev. 2001 Jul;108(3):550-92. doi: 10.1037/0033-295x.108.3.550. PMID: 11488378. + + Args: + lca_layer: The LCALayer that computes the integration for each timestep. + num_lca_dim: The number of LCA units or accumulators. + num_simulations: The number of parallel independent simulations to run. + num_time_steps: The total number of time steps to run. + + """ + + super().__init__() + + self.lca_layer = lca_layer + self.num_lca_dim = num_lca_dim + self.num_simulations = num_simulations + self.num_time_steps = num_time_steps + + def forward(self, ff_input: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + """ + + Args: + ff_input: A vector specifying the feed forward input for each accumulator. + This is typically a representation of the stimulus, task, or both. + + Returns: + A tuple with two vectors, the activations before and after applying the non-linear + activation function. + """ + dev = ff_input.device + + # Mark all accumulators as active by default + active = torch.ones(size=[int(self.num_simulations), 1], device=dev) + + pre_activities = torch.zeros( + size=[int(self.num_simulations), int(self.num_lca_dim)], device=dev + ) + activities = torch.zeros( + size=[int(self.num_simulations), int(self.num_lca_dim)], device=dev + ) + + rts = torch.zeros(size=[int(self.num_simulations), 1], device=dev) + + # Simulate N time steps of the model. This could be done with a while(active) type loop but this is slightly + # faster actually on the GPU. + for time_i in range(int(self.num_time_steps)): + + # Compute the LCA activities + pre_activities, activities, active = self.lca_layer( + ff_input=ff_input, pre_activities=pre_activities, activities=activities + ) + + # Only increment reaction time for active simulations + rts = rts + active + + # Convert the time step index to actual time + rts = rts * self.lca_layer.time_step_size + + # Include the non-decision time in the reaction time. + rts = rts + self.lca_layer.non_decision_time + + # Figure out which accumulator crossed the threshold, this is the decision + torch.max(activities, dim=1) + + max_values, decisions = torch.max(activities, dim=1, keepdim=True) + + # Find any simulations that had multiple accumulators cross the threshold at the same time. + # Exclude these for simplicity. + # good = torch.logical_and( + # torch.sum(activities == max_values, dim=1) == 1, ~torch.squeeze(active) + # ) + # decisions = decisions[good] + # rts = rts[good] + + return rts, decisions + +if __name__ == "__main__": + + lca_params = dict( + threshold=0.06, + leak=10.0, + competition=0.0, + self_excitation=6.2, + non_decision_time=0.0, + noise=0.1, + # noise=None, + time_step_size=0.001, + ) + + # Convert all the lca parameters to torch.FloatTensors + lca_params = {k: torch.tensor(v, device=def_dev) for k, v in lca_params.items()} + + + lca = LCAModel( + lca_layer=LCALayer( + **lca_params, + activation_function=torch.relu, + ), + num_lca_dim=torch.tensor(2), + num_simulations=torch.tensor(50000), + num_time_steps=torch.tensor(3000), + ) + + # Compile things for a bit of a performance boost + lca = torch.jit.script(lca) + + # Create some input for the LCA, typically this would be generated from another part of the model + # that processes stimulus or tasks information into an N sized vector, where N is the number of + # dimensions in the LCA accumulator. + input_array = [1.02, 1.02 + 0.02] + ff_input = torch.Tensor(input_array).to(def_dev) + + # Run things to get some example outputs. + outputs = lca(ff_input) + + torch.onnx.export(lca, # model being run + ff_input, # model input (or a tuple for multiple inputs) + "lca.onnx", # where to save the model (can be a file or file-like object) + export_params=True, # store the trained parameter weights inside the model file + opset_version=13, # the ONNX version to export the model to + do_constant_folding=False, # whether to execute constant folding for optimization + input_names=['ff_input'], # the model's input names + output_names=['rts', 'decisions'], # the model's output names + #operator_export_type=torch.onnx.OperatorExportTypes.ONNX_ATEN_FALLBACK, + example_outputs=outputs, + # dynamic_axes={'input': {0: 'batch_size'}, # variable length axes + # 'output': {0: 'batch_size'}} + ) + + onnx_model = onnx.load("lca.onnx") + onnx.checker.check_model(onnx_model) diff --git a/Scripts/Debug/lca/pytorch_lca.py b/Scripts/Debug/lca/pytorch_lca.py new file mode 100644 index 00000000000..cb70c6639ad --- /dev/null +++ b/Scripts/Debug/lca/pytorch_lca.py @@ -0,0 +1,447 @@ +import torch +import numpy as np +import time + +import matplotlib.pyplot as plt +import seaborn as sns +import pandas as pd + + +from typing import Union, Iterable, Tuple, Optional, Callable + +import psyneulink as pnl + + +if torch.cuda.is_available(): + def_dev = "cuda:0" +else: + def_dev = "cpu" + + +class LCALayer(torch.nn.Module): + def __init__( + self, + threshold: Union[float, None] = 1.0, + leak: float = 0.1, + competition: float = 0.1, + self_excitation: float = 0.0, + non_decision_time: float = 0.0, + activation_function: Callable = torch.relu, + noise: Union[float, torch.Tensor, None] = 1.0, + time_step_size: float = 0.01, + dev: Optional[Union[str, torch.device]] = "cpu" + ): + """ + An implementation of a Leaky Competing Accumulator as a layer. Each call to forward of this module only + implements one time step of the integration. See module LCAModel if you want to simulate an LCA to completion. + + Args: + threshold: The threshold that accumulators must reach to stop integration. If None, accumulators will + never stop integrating even when the pass the threshold. + leak: The decay rate, which reflects leakage of the activation. + competition: The weight to apply for inhibitory influence from other activations. Positive values lead + to inhibitory effects from other accumulators. + self_excitation: The weight to apply for the scaling factor for the recurrent excitation + non_decision_time: The time that should be added to reaction times to account for stimulus encoding and + response generation. This parameter shifts the results reactions times by and offset. This parameter + is not actually used by LCALayer since the forward method only computes a timestep at a time. However, + this is a common LCA model parameter so it should be stored with the others. It is added to final + reaction time generated from LCAModel.forward. + activation_function: The non-linear function to apply to pre activities to get activities. This is + torch.relu by default, but can be any callable. + noise: The standard deviation of the Gaussian noise added to each particles position at each time step. + time_step_size: The time step size (in seconds) for the integration process. + dev: Device to run compute on. + + """ + super().__init__() + + require_grad = False + + self.leak = leak + self.competition = competition + self.self_excitation = self_excitation + self.noise = noise + self.time_step_size = time_step_size + self.non_decision_time = non_decision_time + self._sqrt_step_size = torch.sqrt( + torch.tensor(0.001, requires_grad=require_grad).to(dev) + ) + self.threshold = threshold + self.activation_function = activation_function + self.dev = dev + + def forward( + self, + ff_input: torch.Tensor, + pre_activities: torch.Tensor, + activities: torch.Tensor, + ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """ + Compute one time step of integration for a batch of independent leaky competing accumulator + instances. + + Args: + ff_input: The current input activity for the accumulators + pre_activities: The activity of the accumulator without the application of the non-linearity + activation_func. A 2D tensors of shape [batch_size, num_accumulators]. batch_size in + this case is each independent accumulator model instance. Or in other words, a simulation of the model. + activities: The current activity of each accumulator instance. This is the value after + the non-linearity activation_func has been applied to pre_activities. A 2D tensors of shape + [batch_size, num_accumulators]. batch_size in this case is each independent accumulator + model instance. Or in other words, a simulation of the model. + + Returns: + The current output activities of the accumulators, of same shape as activities. + """ + num_simulations, num_lca_dim = activities.shape + + # Mark all accumulators as active by default + active = torch.ones(size=(num_simulations, 1), device=self.dev) + + # If threshold is provided, only integrate accumulators until they reach the threshold. + if self.threshold is not None: + active = torch.all( + torch.abs(activities) < self.threshold, dim=1, keepdim=True + ) + + # Construct a gamma matrix, this is multiplied by each accumulator vector to compute + # the competitive inhibition and self excitation between units. + gamma = (torch.eye(num_lca_dim, device=self.dev) == 0.0) * self.competition + gamma.fill_diagonal_(-self.self_excitation) + + # Perform one time step of the integration + pre_activities = ( + pre_activities + + (ff_input - self.leak * pre_activities - torch.matmul(activities, gamma)) + * active + * self.time_step_size + ) + + # If standard deviation of noise is provided. Generate a noise for each accumulator. + # Only active accumulators will get noise + if self.noise is not None: + dw = torch.normal( + mean=torch.zeros(activities.size(), device=self.dev), + std=active * self.noise * torch.ones(activities.size(), device=self.dev), + ) + pre_activities = pre_activities + dw * self._sqrt_step_size + + # Calculate the post activation function activities. Don't overwrite pre_activities, we will need these for + # the next timestep. + activities = self.activation_function(pre_activities) + + return pre_activities, activities, active + + +class LCAModel(torch.nn.Module): + def __init__( + self, + lca_layer: LCALayer, + num_lca_dim: int, + num_simulations: int = 10000, + num_time_steps: int = 3000, + save_activities: bool = False, + dev: Optional[Union[str, torch.device]] = "cpu" + ): + """ + A model that simulates a leaky competing accumulator model (Usher and McClelland). + + References: + + Usher M, McClelland JL. The time course of perceptual choice: the leaky, competing accumulator model. + Psychol Rev. 2001 Jul;108(3):550-92. doi: 10.1037/0033-295x.108.3.550. PMID: 11488378. + + Args: + lca_layer: The LCALayer that computes the integration for each timestep. + num_lca_dim: The number of LCA units or accumulators. + num_simulations: The number of parallel independent simulations to run. + num_time_steps: The total number of time steps to run. + save_activities: Should activities be saved. If True, make sure the number of simulations is low. + + """ + + super().__init__() + + self.lca_layer = lca_layer + self.num_lca_dim = num_lca_dim + self.num_simulations = num_simulations + self.num_time_steps = num_time_steps + self.save_activities = save_activities + self.dev = dev + + def forward(self, ff_input: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + """ + + Args: + ff_input: A vector specifying the feed forward input for each accumulator. + This is typically a representation of the stimulus, task, or both. + + Returns: + A tuple with two vectors, the activations before and after applying the non-linear + activation function. + """ + + # Mark all accumulators as active by default + active = torch.ones(size=(self.num_simulations, 1), device=self.dev) + + if self.save_activities: + pre_activities = torch.zeros( + size=(self.num_simulations, self.num_lca_dim, self.num_time_steps + 1), + device=self.dev, + ) + activities = torch.zeros( + size=(self.num_simulations, self.num_lca_dim, self.num_time_steps + 1), + device=self.dev, + ) + else: + pre_activities = torch.zeros( + size=(self.num_simulations, self.num_lca_dim), device=self.dev + ) + activities = torch.zeros( + size=(self.num_simulations, self.num_lca_dim), device=self.dev + ) + + rts = torch.zeros(size=(self.num_simulations, 1), device=self.dev) + + # Simulate N time steps of the model. This could be done with a while(active) type loop but this is slightly + # faster actually on the GPU. + for time_i in range(self.num_time_steps): + + # Compute the LCA activities + if self.save_activities: + ( + pre_activities[:, :, time_i + 1], + activities[:, :, time_i + 1], + active, + ) = self.lca_layer( + ff_input=ff_input, + pre_activities=pre_activities[:, :, time_i], + activities=activities[:, :, time_i], + ) + else: + pre_activities, activities, active = self.lca_layer( + ff_input=ff_input, pre_activities=pre_activities, activities=activities + ) + + # Only increment reaction time for active simulations + rts = rts + active + + # Convert the time step index to actual time + rts = rts * self.lca_layer.time_step_size + + # Include the non-decision time in the reaction time. + rts = rts + self.lca_layer.non_decision_time + + # Figure out which accumulator crossed the threshold, this is the decision + torch.max(activities, dim=1) + + if not self.save_activities: + max_values, decisions = torch.max(activities, dim=1, keepdim=True) + + # Find any simulations that had multiple accumulators cross the threshold at the same time. + # Exclude these for simplicity. + good = torch.logical_and( + torch.sum(activities == max_values, dim=1) == 1, ~torch.squeeze(active) + ) + decisions = decisions[good] + rts = rts[good] + + else: + decisions = torch.argmax(activities[:, :, self.num_time_steps], dim=1) + + if self.save_activities: + return pre_activities, activities + else: + return rts, decisions + + +def make_pnl_lca( + num_lca_dim: int = 2, + threshold: Union[float, None] = 1.0, + leak: float = 0.1, + competition: float = 0.1, + self_excitation: float = 0.0, + non_decision_time: float = 0.0, + activation_function: Callable = pnl.ReLU, + noise: Union[float, torch.Tensor, None] = 1.0, + time_step_size: float = 0.01, +): + + if noise is not None: + noise_func = lambda: rng.normal(loc=0.0, scale=noise) + else: + noise_func = 0.0 + + lca = pnl.LCAMechanism( + default_variable=[[0.0 for _ in range(num_lca_dim)]], + size=num_lca_dim, + threshold=threshold, + function=activation_function, + leak=leak, + competition=competition, + self_excitation=self_excitation, + noise=noise_func, + time_step_size=time_step_size, + termination_measure=max, + termination_comparison_op=">=", + reset_stateful_function_when=pnl.AtTrialStart(), + # execute_until_finished=True, + # max_executions_before_finished=sys.maxsize, + name="LCA", + ) + + lca.set_log_conditions([pnl.RESULT]) + + comp = pnl.Composition(name="LCA-Comp") + comp.add_node(lca) + + return comp, lca + + +if __name__ == "__main__": + + # Set to true if you want to plot an example histories of activations + save_activities = False + + if save_activities: + num_simulations = 10 + else: + num_simulations = 50000 + + lca_params = dict( + threshold=0.06, + leak=10.0, + competition=0.0, + self_excitation=6.2, + non_decision_time=0.0, + noise=0.1, + # noise=None, + time_step_size=0.001, + ) + + lca = LCAModel( + lca_layer=LCALayer( + **lca_params, + activation_function=torch.relu, + dev=def_dev + ), + num_lca_dim=2, + num_simulations=num_simulations, + num_time_steps=3000, + save_activities=save_activities, + dev=def_dev + ) + + # Compile things for a bit of a performance boost + lca = torch.jit.script(lca) + + rng = np.random.RandomState(seed=42) + comp, pnl_lca_mech = make_pnl_lca( + num_lca_dim=2, activation_function=pnl.ReLU, **lca_params + ) + + # Create some input for the LCA, typically this would be generated from another part of the model + # that processes stimulus or tasks information into an N sized vector, where N is the number of + # dimensions in the LCA accumulator. + input_array = [1.02, 1.02 + 0.02] + input_pnl = {pnl_lca_mech: [input_array]} + ff_input = torch.Tensor(input_array).to(def_dev) + + # Run the PNL LCA + comp.run(input_pnl, execution_mode=pnl.ExecutionMode.Python, num_trials=10) + pnl_results = comp.results + + # Get the LCA activities from the log if running in non-compiled mode. Just get one trial's worth + pnl_activities = np.array( + [ + t.value.tolist() + for t in pnl_lca_mech.log.get_logged_entries()["RESULT"]["LCA-Comp"] + if t.time.trial == 0 + ] + ) + + # Warm things up before running things for timing + lca(ff_input) + lca(ff_input) + + t0 = time.time() + + # Run things ten times so we can get a rough average execution time. + for i in range(10): + # Run many independent simulations of the same LCA model + if save_activities: + pre_activities, activities = lca(ff_input=ff_input) + activities = activities.to("cpu").numpy() + pre_activities = pre_activities.to("cpu").numpy() + else: + rts, decisions = lca(ff_input=ff_input) + rts = rts.to("cpu").numpy().flatten() + decisions = decisions.to("cpu").numpy().flatten() + + t1 = time.time() + print(f"Average Elapsed: {(t1-t0) / 10.0} seconds") + + + if save_activities: + pre_activities = np.transpose(pre_activities[0, :, :]) + activities = np.transpose(activities[0, :, :]) + rt_i = max(np.argmax(activities, axis=0)) + 1 + activities = activities[0:rt_i, :] + pre_activities = pre_activities[0:rt_i, :] + preact_df = pd.DataFrame( + pre_activities, columns=[f"{i}" for i in range(pre_activities.shape[1])] + ) + preact_df["name"] = "pre activation" + preact_df["t"] = np.arange(rt_i) * lca.lca_layer.time_step_size + act_df = pd.DataFrame( + activities, columns=[f"{i}" for i in range(activities.shape[1])] + ) + act_df["name"] = "post activation" + act_df["t"] = np.arange(rt_i) * lca.lca_layer.time_step_size + results = pd.concat([preact_df, act_df]) + results = pd.melt(results, id_vars=["name", "t"], var_name="lca_unit") + + # Add the PNL results + for i in range(lca.num_lca_dim): + results = pd.concat( + [ + results, + pd.DataFrame( + { + "name": "PNL post activation", + "t": (np.arange(pnl_activities.shape[0]) + 1) + * lca.lca_layer.time_step_size, + "lca_unit": str(i), + "value": pnl_activities[:, i], + } + ), + ] + ) + + #%% + g = sns.FacetGrid( + results[results["name"].str.contains("post activation")], + col="lca_unit", + hue="name", + ) + g.map_dataframe(sns.lineplot, x="t", y="value") + g.set_axis_labels("Time (s)", "Activity") + g.add_legend() + + ax1, ax2 = g.axes[0] + + ax1.axhline(lca.lca_layer.threshold, ls="--") + ax2.axhline(lca.lca_layer.threshold, ls="--") + + ax1.axvline((rt_i - 1) * lca.lca_layer.time_step_size, ls="--") + ax2.axvline((rt_i - 1) * lca.lca_layer.time_step_size, ls="--") + + plt.show() + #%% + + else: + results = pd.DataFrame({"reaction_time": rts, "decision": decisions}) + sns.kdeplot(data=results, x="reaction_time", hue="decision") + plt.xlim([0.0, 3.5]) + plt.show() From a3c0f6c11ae241ad5dca817786750e8193158a09 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 7 Nov 2021 18:25:37 -0500 Subject: [PATCH 041/285] =?UTF-8?q?=E2=80=A2=20optimizationcontrolmechanis?= =?UTF-8?q?m.py:=20=20=20added=20feature=5Finput=5Fports=20attribute=20and?= =?UTF-8?q?=20num=5Ffeature=5Finput=5Fports=20property?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modulatory/control/controlmechanism.py | 12 +++--- .../control/optimizationcontrolmechanism.py | 40 +++++++++++++++---- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index b098957d896..eb131ab6f27 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1889,18 +1889,18 @@ def monitored_output_ports(self, value): return None @property - def num_outcome_input_ports(self): + def monitored_output_ports_weights_and_exponents(self): try: - return len(self.outcome_input_ports) + return self.objective_mechanism.monitored_output_ports_weights_and_exponents except: - return 0 + return None @property - def monitored_output_ports_weights_and_exponents(self): + def num_outcome_input_ports(self): try: - return self.objective_mechanism.monitored_output_ports_weights_and_exponents + return len(self.outcome_input_ports) except: - return None + return 0 @property def control_signals(self): diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 217fdf1c4ae..9a0a24b89ba 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -256,13 +256,14 @@ *State Features* ^^^^^^^^^^^^^^^^ -In addition to its `primary InputPort ` (which typically receives a projection from the -*OUTCOME* OutputPort of the `objective_mechanism `, an -OptimizationControlMechanism also has an `InputPort` for each of its state_features. By default, these are the current -`input ` for the Composition to which the OptimizationControlMechanism belongs. However, -different values can be specified, as can a `state_feature_function ` -that transforms these. For OptimizationControlMechanisms that implement `model-free -` optimization, its `state_feature_values +In addition to its `outcome_input_ports ` (that receive Projections from +either the OptimizationControlMechanism's `objective_mechanism ` or directly +from the items in `monitor_for_control `), it also has an `InputPort` for each of its +state_features, listed in its `feature_input_ports ` attribute. +By default, these are the current `input ` for the Composition to which the +OptimizationControlMechanism belongs. However, different values can be specified, as can a `state_feature_function +` that transforms these. For OptimizationControlMechanisms that +implement `model-free ` optimization, its `state_feature_value ` are used by its `evaluate_agent_rep ` method to predict the `net_outcome ` for a given `control_allocation `. For OptimizationControlMechanisms that @@ -511,7 +512,7 @@ from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.sampleiterator import SampleIterator, SampleSpec -from psyneulink.core.globals.utilities import convert_to_list, convert_to_np_array +from psyneulink.core.globals.utilities import convert_to_list, convert_to_np_array, ContentAddressableList __all__ = [ 'OptimizationControlMechanism', 'OptimizationControlMechanismError', @@ -650,6 +651,15 @@ class OptimizationControlMechanism(ControlMechanism): the current value of each item of the OptimizationControlMechanism's `state_features ` (each of which is a 1d array). + feature_input_ports : ContentAddressableList + lists the OptimizationControlMechanism's `InputPorts ` that receive `Projections ` + from the items specified in the **state_features** argument in the OptimizationControlMechanism's constructor, + and provide its `state_feature_values ` (see + `OptimizationControlMechanism_State_Features` for additional details). + + num_feature_input_ports : int + cantains the number of `feature_input_ports `. + agent_rep : Composition determines the `Composition` used by the `evaluate_agent_rep ` method to predict the `net_outcome ` for a given `state @@ -1040,6 +1050,13 @@ def _instantiate_input_ports(self, context=None): self.state_feature_function) super()._instantiate_input_ports(feature_input_ports, context=context) + + # Assign to self.feature_input_ports + start = self.num_outcome_input_ports # FIX: 11/3/21 NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED + stop = start + len(feature_input_ports) if feature_input_ports else 0 + self.feature_input_ports = ContentAddressableList(component_type=InputPort, + list=self.input_ports[start:stop]) + for i in range(1, len(self.input_ports)): port = self.input_ports[i] if len(port.path_afferents) > 1: @@ -1664,6 +1681,13 @@ def _parse_state_feature_specs(self, feature_input_ports, feature_function, cont return parsed_features + @property + def num_feature_input_ports(self): + try: + return len(self.feature_input_ports) + except: + return 0 + @property def _model_spec_parameter_blacklist(self): # default_variable is hidden in constructor arguments, From 7069b3a7bb8b347dd487ee3d7b5539d1532178f8 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 7 Nov 2021 18:29:36 -0500 Subject: [PATCH 042/285] =?UTF-8?q?=E2=80=A2=20optimizationcontrolmechanis?= =?UTF-8?q?m.py:=20=20=20added=20feature=5Finput=5Fports=20attribute=20and?= =?UTF-8?q?=20num=5Ffeature=5Finput=5Fports=20property?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute --- .../parameterestimationcomposition.py | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/psyneulink/core/compositions/parameterestimationcomposition.py b/psyneulink/core/compositions/parameterestimationcomposition.py index 06a50d2e1b0..20816237196 100644 --- a/psyneulink/core/compositions/parameterestimationcomposition.py +++ b/psyneulink/core/compositions/parameterestimationcomposition.py @@ -185,33 +185,6 @@ def _same_seed_for_all_parameter_combinations_setter(value, owning_component, co return value -class Parameters(Composition.Parameters): - """ - Attributes - ---------- - - initial_seed - see `input_specification ` - - :default value: None - :type: ``int`` - - same_seed_for_all_parameter_combinations - see `input_specification ` - - :default value: False - :type: ``bool`` - - """ - # FIX: 11/32/21 CORRECT INITIAlIZATIONS? - initial_seed = Parameter(None, loggable=False, pnl_internal=True, - getter=_initial_seed_getter, - setter=_initial_seed_setter) - same_seed_for_all_parameter_combinations = Parameter(False, loggable=False, pnl_internal=True, - getter=_same_seed_for_all_parameter_combinations_getter, - setter=_same_seed_for_all_parameter_combinations_setter) - - class ParameterEstimationComposition(Composition): """ Composition( \ @@ -423,6 +396,32 @@ class ParameterEstimationComposition(Composition): found to best fit the data. """ + class Parameters(Composition.Parameters): + """ + Attributes + ---------- + + initial_seed + see `input_specification ` + + :default value: None + :type: ``int`` + + same_seed_for_all_parameter_combinations + see `input_specification ` + + :default value: False + :type: ``bool`` + + """ + # FIX: 11/32/21 CORRECT INITIAlIZATIONS? + initial_seed = Parameter(None, loggable=False, pnl_internal=True, + getter=_initial_seed_getter, + setter=_initial_seed_setter) + same_seed_for_all_parameter_combinations = Parameter(False, loggable=False, pnl_internal=True, + getter=_same_seed_for_all_parameter_combinations_getter, + setter=_same_seed_for_all_parameter_combinations_setter) + def __init__(self, parameters, # OCM control_signals outcome_variables, # OCM monitor_for_control From 65b960e639aafd5459d1ed4af569bdba3c0ffbbf Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 7 Nov 2021 18:36:54 -0500 Subject: [PATCH 043/285] =?UTF-8?q?=E2=80=A2=20optimizationcontrolmechanis?= =?UTF-8?q?m.py:=20=20=20added=20feature=5Finput=5Fports=20attribute=20and?= =?UTF-8?q?=20num=5Ffeature=5Finput=5Fports=20property?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter --- .../nonstateful/optimizationfunctions.py | 22 ++++++++++++++----- .../test_parameterestimationcomposition.py | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 464299f0748..024198a230b 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -71,6 +71,13 @@ def __init__(self, error_value): self.error_value = error_value +def _num_estimates_getter(owning_component, context): + if owning_component.parameters.randomization_dimension._get(context) is None: + return 1 + else: + return owning_component.parameters.search_space._get(context)[owning_component.randomization_dimension].num + + class OptimizationFunction(Function_Base): """ OptimizationFunction( \ @@ -308,6 +315,12 @@ class Parameters(Function_Base.Parameters): :default value: None :type: + num_estimates + see `num_estimates ` + + :default value: None + :type: ``int`` + objective_function see `objective_function ` @@ -371,6 +384,9 @@ class Parameters(Function_Base.Parameters): search_termination_function = Parameter(lambda x, y, z: True, stateful=False, loggable=False) search_space = Parameter([SampleIterator([0])], stateful=False, loggable=False) randomization_dimension = Parameter(None, stateful=False, loggable=False) + num_estimates = Parameter(None, stateful=True, loggable=True, read_only=True, + dependencies=[randomization_dimension, search_space], + getter=_num_estimates_getter) save_samples = Parameter(False, pnl_internal=True) save_values = Parameter(False, pnl_internal=True) @@ -642,12 +658,6 @@ def _report_value(self, new_value): """Report value returned by `objective_function ` for sample.""" pass - @property - def num_estimates(self): - if self.randomization_dimension is None: - return 1 - else: - return self.search_space[self.randomization_dimension].num class GridBasedOptimizationFunction(OptimizationFunction): """Implement helper method for parallelizing instantiation for evaluating samples from search space.""" diff --git a/tests/composition/test_parameterestimationcomposition.py b/tests/composition/test_parameterestimationcomposition.py index 8f3b3841d42..dbf7ea8df2c 100644 --- a/tests/composition/test_parameterestimationcomposition.py +++ b/tests/composition/test_parameterestimationcomposition.py @@ -102,6 +102,7 @@ def test_parameter_estimation_composition(objective_function_arg, expected_input assert not ctlr.objective_mechanism # For objective_function specified assert len(ctlr.input_ports[pnl.OUTCOME].variable) == expected_input_len assert len(ctlr.control_signals) == 3 + assert ctlr.function.num_estimates == 3 assert pnl.RANDOMIZATION_CONTROL_SIGNAL_NAME in ctlr.control_signals.names assert ctlr.control_signals[pnl.RANDOMIZATION_CONTROL_SIGNAL_NAME].allocation_samples.base.num == 3 # pec.run() From a8d428be3eda405281aadfad5d5caee97629d3ce Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 7 Nov 2021 19:59:27 -0500 Subject: [PATCH 044/285] - modified test_mode_based_num_estimates --- .../control/optimizationcontrolmechanism.py | 14 +++++++------- tests/composition/test_control.py | 4 ++-- .../test_parameterestimationcomposition.py | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 9a0a24b89ba..87c51992dd8 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -516,12 +516,12 @@ __all__ = [ 'OptimizationControlMechanism', 'OptimizationControlMechanismError', - 'AGENT_REP', 'STATE_FEATURES', 'RANDOMIZATION_CONTROL_SIGNAL_NAME' + 'AGENT_REP', 'STATE_FEATURES', 'RANDOMIZATION_CONTROL_SIGNAL' ] AGENT_REP = 'agent_rep' STATE_FEATURES = 'state_features' -RANDOMIZATION_CONTROL_SIGNAL_NAME = 'RANDOMIZATION_CONTROL_SIGNAL' +RANDOMIZATION_CONTROL_SIGNAL = 'RANDOMIZATION_CONTROL_SIGNAL' def _parse_state_feature_values_from_variable(variable): return convert_to_np_array(np.array(variable[1:]).tolist()) @@ -1112,7 +1112,7 @@ def random_integer_generator(): seed_param_ports = [param._port for param in self.agent_rep.all_dependent_parameters('seed').keys()] # Construct ControlSignal to modify seeds over estimates - self.output_ports.append(ControlSignal(name=RANDOMIZATION_CONTROL_SIGNAL_NAME, + self.output_ports.append(ControlSignal(name=RANDOMIZATION_CONTROL_SIGNAL, modulates=seed_param_ports, allocation_samples=random_seeds)) @@ -1162,16 +1162,16 @@ def _instantiate_attributes_after_function(self, context=None): # If there is no randomization_control_signal, but num_estimates is 1 or None, # pass None for randomization_control_signal_index (1 will be used by default by OptimizationFunction) - if RANDOMIZATION_CONTROL_SIGNAL_NAME not in self.control_signals and self.num_estimates in {1, None}: + if RANDOMIZATION_CONTROL_SIGNAL not in self.control_signals and self.num_estimates in {1, None}: randomization_control_signal_index = None # Otherwise, assert that num_estimates and number of seeds generated by randomization_control_signal are equal else: - num_seeds = self.control_signals[RANDOMIZATION_CONTROL_SIGNAL_NAME].allocation_samples.base.num + num_seeds = self.control_signals[RANDOMIZATION_CONTROL_SIGNAL].allocation_samples.base.num assert self.num_estimates == num_seeds, \ f"PROGRAM ERROR: The value of the 'num_estimates' Parameter of {self.name}" \ f"({self.num_estimates}) is not equal to the number of estimates that will be generated by " \ - f"its {RANDOMIZATION_CONTROL_SIGNAL_NAME} ControlSignal ({num_seeds})." - randomization_control_signal_index = self.control_signals.names.index(RANDOMIZATION_CONTROL_SIGNAL_NAME) + f"its {RANDOMIZATION_CONTROL_SIGNAL} ControlSignal ({num_seeds})." + randomization_control_signal_index = self.control_signals.names.index(RANDOMIZATION_CONTROL_SIGNAL) # Assign parameters to function (OptimizationFunction) that rely on OptimizationControlMechanism self.function.reset(**{ diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 86e235b26f9..43c45154fb8 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -2168,8 +2168,7 @@ def test_model_based_num_estimates(self): state_features=[A.input_port], objective_mechanism=objective_mech, function=pnl.GridSearch(), - # num_estimates=5, - num_estimates=None, + num_estimates=5, # <- Results are same as =1 since no noise parameters) control_signals=[control_signal]) comp.add_controller(ocm) @@ -2179,6 +2178,7 @@ def test_model_based_num_estimates(self): comp.run(inputs=inputs, num_trials=2) + assert not comp.controller.control_signals[pnl.RANDOMIZATION_CONTROL_SIGNAL].efferents # Confirm no noise assert np.allclose(comp.simulation_results, [[np.array([2.25])], [np.array([3.5])], [np.array([4.75])], [np.array([3.])], [np.array([4.25])], [np.array([5.5])]]) assert np.allclose(comp.results, diff --git a/tests/composition/test_parameterestimationcomposition.py b/tests/composition/test_parameterestimationcomposition.py index dbf7ea8df2c..2f01a18318e 100644 --- a/tests/composition/test_parameterestimationcomposition.py +++ b/tests/composition/test_parameterestimationcomposition.py @@ -103,6 +103,6 @@ def test_parameter_estimation_composition(objective_function_arg, expected_input assert len(ctlr.input_ports[pnl.OUTCOME].variable) == expected_input_len assert len(ctlr.control_signals) == 3 assert ctlr.function.num_estimates == 3 - assert pnl.RANDOMIZATION_CONTROL_SIGNAL_NAME in ctlr.control_signals.names - assert ctlr.control_signals[pnl.RANDOMIZATION_CONTROL_SIGNAL_NAME].allocation_samples.base.num == 3 + assert pnl.RANDOMIZATION_CONTROL_SIGNAL in ctlr.control_signals.names + assert ctlr.control_signals[pnl.RANDOMIZATION_CONTROL_SIGNAL].allocation_samples.base.num == 3 # pec.run() From 1218b6f4cf79b411d33bff28e1a008c175a40f53 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 7 Nov 2021 22:05:12 -0500 Subject: [PATCH 045/285] - --- .../compositions/parameterestimationcomposition.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/compositions/parameterestimationcomposition.py b/psyneulink/core/compositions/parameterestimationcomposition.py index 49aa7d082a0..2c4acfc958c 100644 --- a/psyneulink/core/compositions/parameterestimationcomposition.py +++ b/psyneulink/core/compositions/parameterestimationcomposition.py @@ -117,7 +117,10 @@ * **objective_function** - specifies a function used to evaluate the `values ` of the `outcome_variables `, according to which combinations of - `parameters ` are assessed. + `parameters ` are assessed. The shape of the `variable + ` of the `objective_function (i.e., its first positional argument) must be the same as an + array containing the `value ` of the OutputPort corresponding to each item specified in + `outcome_variables `. * **optimization_function** - specifies the function used to search over values of the `parameters ` in order to optimize the **objective_function**. It can be any @@ -246,7 +249,10 @@ class ParameterEstimationComposition(Composition): specifies the function used to evaluate the `fit to data ` or `optimize ` the parameters of the `model ` according to a specified `objective_function - `. + `; the shape of its `variable ` of the + `objective_function (i.e., its first positional argument) must be the same as an array containing the `value + ` of the OutputPort corresponding to each item specified in `outcome_variables + `. num_estimates : int : default 1 specifies the number of estimates made for a each combination of `parameter ` From 41567bb6315bab3f3a8e9405fe0336ba462fa016 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 7 Nov 2021 22:58:01 -0500 Subject: [PATCH 046/285] - --- .../control/optimizationcontrolmechanism.py | 28 ++++++++++++++----- .../test_parameterestimationcomposition.py | 17 ++++++++--- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 87c51992dd8..05bfc23a788 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -161,20 +161,33 @@ `, to calculate a `net_outcome `. For `model-based optimization ` these are also used as the inputs to the Composition (i.e., `agent_rep `) when it's `evaluate ` - method is called (see `OptimizationControlMechanism_State_Features` below). Features can be specified using - any of the following, singly or combined in a list: + method is called (see `OptimizationControlMechanism_State_Features` below). By default, these are the current + `input ` for the Composition to which the OptimizationControlMechanism belongs. However, + other values can be specified using either or both of the following, either singly or in a list: * *InputPort specification* -- this can be any form of `InputPort specification ` that resolves to an OutputPort from which the InputPort receives a Projection; the `value ` of that OutputPort is used as one of the `state_feature_values ` for the `state_features - ` of the OptimizationControlMechanism. Each of these InputPorts is - marked as `internal_only ` = `True`. + ` of the OptimizationControlMechanism. + + .. technical_note:: + The InputPorts specified as state_features are marked as `internal_only ` = `True`; + + * *Output specification* -- this can be any form of `OutputPort specification ` + for any `OutputPort` of another `Mechanism ` in the Composition; the `value ` + of the specified OutputPort is used as one of the `state_feature_values + ` for the `state_features + ` of the OptimizationControlMechanism. Features can also be added to an existing OptimizationControlMechanism using its `add_state_features` method. If the **state_features** argument is not specified, then the `input ` to the `Composition` on - the last trial of its execution is used to predict the `net_outcome ` for the upcoming - trial. + the last trial executed is used to predict the `net_outcome ` for the upcoming trial; + COMMENT: + FIX: CONFIRM THE FOLLOWING + COMMENT + if `controller_mode ` = *AFTER*, the input used is from the trial just + executed; if it is *BEFORE*, then it is from the previous trial. .. _OptimizationControlMechanism_Feature_Function_Arg: @@ -262,7 +275,8 @@ state_features, listed in its `feature_input_ports ` attribute. By default, these are the current `input ` for the Composition to which the OptimizationControlMechanism belongs. However, different values can be specified, as can a `state_feature_function -` that transforms these. For OptimizationControlMechanisms that +` that transforms these (see `above +`). For OptimizationControlMechanisms that implement `model-free ` optimization, its `state_feature_value ` are used by its `evaluate_agent_rep ` method to predict the `net_outcome ` diff --git a/tests/composition/test_parameterestimationcomposition.py b/tests/composition/test_parameterestimationcomposition.py index 2f01a18318e..fd3b476b21a 100644 --- a/tests/composition/test_parameterestimationcomposition.py +++ b/tests/composition/test_parameterestimationcomposition.py @@ -23,10 +23,10 @@ # objective_function = {None: 2, Concatenate: 2, LinearCombination: 1} # expected -pec_test_args = [(None, 2, True, False), - (None, 2, False, True), - (Concatenate, 2, True, False), - (LinearCombination, 1, True, False), +pec_test_args = [(None, 2, True, False), # No ObjectiveMechanism (2 inputs), model arg + (None, 2, False, True), # No ObjectiveMechanism (2 inputs), nodes arg + (Concatenate, 2, True, False), # ObjectiveMechanism (2 inputs), model arg + (LinearCombination, 1, True, False), # ObjectiveMechanism (1 input), model arg # (None, 2, True, True), <- USE TO TEST ERROR # (None, 2, False, False), <- USE TO TEST ERROR ] @@ -37,6 +37,7 @@ ids=[f"{x[0]}-{'model' if x[2] else None}-{'nodes' if x[3] else None})" for x in pec_test_args] ) def test_parameter_estimation_composition(objective_function_arg, expected_input_len, model_spec, node_spec): + """Test with and without ObjectiveMechanism specified, and use of model vs. nodes arg of constructor""" samples = np.arange(0.1, 1.01, 0.3) Input = pnl.TransferMechanism(name='Input') reward = pnl.TransferMechanism(output_ports=[pnl.RESULT, pnl.MEAN, pnl.VARIANCE], @@ -83,6 +84,8 @@ def test_parameter_estimation_composition(objective_function_arg, expected_input model = comp if model_spec else None, nodes = comp if node_spec else None, # data = [1,2,3], # For testing error + state_features=[Input.input_port, reward.input_port], + state_feature_function=pnl.AdaptiveIntegrator(rate=0.1), parameters={('drift_rate',Decision):[1,2], ('threshold',Decision2):[1,2],}, # parameters={('shrimp_boo',Decision):[1,2], # For testing error @@ -96,6 +99,12 @@ def test_parameter_estimation_composition(objective_function_arg, expected_input # enable_controller=False # For testing error ) ctlr = pec.controller + + assert comp.controller.num_feature_input_ports == 2 + assert comp.controller.feature_input_ports[0].function.name == pnl.AdaptiveIntegrator.__class__.__name__ + assert comp.controller.feature_input_ports[1].function.name == pnl.AdaptiveIntegrator.__class__.__name__ + + assert ctlr.num_outcome_input_ports == 1 if objective_function_arg: assert ctlr.objective_mechanism # For objective_function specified else: From 822742fe8bbd78c76c73f99054e54cf71b9dec91 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Mon, 8 Nov 2021 19:47:05 -0500 Subject: [PATCH 047/285] =?UTF-8?q?=E2=80=A2=20optimizationcontrolmechanis?= =?UTF-8?q?m.py:=20=20=20-=20=5Finstantiate=5Fcontrol=5Fsignals:=20random?= =?UTF-8?q?=5Fseeds=20->=20random=5Fseed=5Fmod=5Fvalues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modulatory/control/optimizationcontrolmechanism.py | 4 ++-- psyneulink/core/compositions/composition.py | 4 ++++ tests/composition/test_parameterestimationcomposition.py | 2 -- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 05bfc23a788..e0710d1f928 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1117,7 +1117,7 @@ def random_integer_generator(): rng = np.random.RandomState() rng.seed(self.initial_seed) return rng.random_integers(self.num_estimates) - random_seeds = SampleSpec(num=self.num_estimates, function=random_integer_generator) + random_seed_modulation_values = SampleSpec(num=self.num_estimates, function=random_integer_generator) # FIX: noise PARAM OF TransferMechanism IS MARKED AS SEED WHEN ASSIGNED A DISTRIBUTION FUNCTION, # BUT IT HAS NO PARAMETER PORT BECAUSE THAT PRESUMABLY IS FOR THE INTEGRATOR FUNCTION, @@ -1128,7 +1128,7 @@ def random_integer_generator(): # Construct ControlSignal to modify seeds over estimates self.output_ports.append(ControlSignal(name=RANDOMIZATION_CONTROL_SIGNAL, modulates=seed_param_ports, - allocation_samples=random_seeds)) + allocation_samples=random_seed_modulation_values)) for i, spec in enumerate(self.output_ports): control_signal = self._instantiate_control_signal(spec, context=context) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index f8a455014f5..91a12311d24 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -7276,8 +7276,12 @@ def _get_control_signals_for_composition(self): control_signal_specs.extend(node._get_parameter_port_deferred_init_control_specs()) return control_signal_specs + # FIX: 11/3/21 GET RID OF THIS ANC CALL TO IT ONCE PROJECTIONS HAVE BEEN IMPLEMENTED FOR SHADOWED INPUTS + # CHECK WHETHER feture_inputs ADD TO OR REPLACE shadowed_inputs def _build_predicted_inputs_dict(self, predicted_input): inputs = {} + # FIX: 11/3/21: outcome_input_ports is now the assumption; + # and feature_input_ports should be assigned for inputs for shadow_inputs # ASSUMPTION: input_ports[0] is NOT a feature and input_ports[1:] are state_features # If this is not a good assumption, we need another way to look up the feature InputPorts # of the OCM and know which InputPort maps to which predicted_input value diff --git a/tests/composition/test_parameterestimationcomposition.py b/tests/composition/test_parameterestimationcomposition.py index fd3b476b21a..cd512203719 100644 --- a/tests/composition/test_parameterestimationcomposition.py +++ b/tests/composition/test_parameterestimationcomposition.py @@ -84,8 +84,6 @@ def test_parameter_estimation_composition(objective_function_arg, expected_input model = comp if model_spec else None, nodes = comp if node_spec else None, # data = [1,2,3], # For testing error - state_features=[Input.input_port, reward.input_port], - state_feature_function=pnl.AdaptiveIntegrator(rate=0.1), parameters={('drift_rate',Decision):[1,2], ('threshold',Decision2):[1,2],}, # parameters={('shrimp_boo',Decision):[1,2], # For testing error From 5a3efba957315328ef508e0ad071bb95fec8e233 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Tue, 9 Nov 2021 08:33:28 -0500 Subject: [PATCH 048/285] =?UTF-8?q?=E2=80=A2=20composition.py=20=20=20-=20?= =?UTF-8?q?=5Fadd=5Fcontroller:=20=20modifying=20to=20instantiate=20featur?= =?UTF-8?q?e=5Finput=5Fports=20if=20none=20are=20specified?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modulatory/control/controlmechanism.py | 2 +- .../control/optimizationcontrolmechanism.py | 53 ++++++++++--------- psyneulink/core/compositions/composition.py | 24 ++++++--- tests/composition/test_control.py | 1 + .../test_parameterestimationcomposition.py | 4 -- 5 files changed, 48 insertions(+), 36 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index eb131ab6f27..44006c2f959 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1485,7 +1485,7 @@ def _instantiate_input_ports(self, input_ports=None, context=None): other_input_ports = input_ports or [] - # FIX 11/3/21: THIS SHOULD BE MODIFIED TO BE A LIST, THAT CONTAINS REFERENCES TO THE OUTCOME InputPorts + # FIX 11/3/21: THIS SHOULD BE MADE A PARAMETER self.outcome_input_ports = ContentAddressableList(component_type=OutputPort) # If ObjectiveMechanism is specified, instantiate it and OUTCOME InputPort that receives projection from it diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index e0710d1f928..f2d6829e2d2 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -269,15 +269,15 @@ *State Features* ^^^^^^^^^^^^^^^^ -In addition to its `outcome_input_ports ` (that receive Projections from +In addition to its `outcome_input_ports `, that receive Projections from either the OptimizationControlMechanism's `objective_mechanism ` or directly -from the items in `monitor_for_control `), it also has an `InputPort` for each of its -state_features, listed in its `feature_input_ports ` attribute. +from the items in `monitor_for_control `, it also has `InputPorts ` for any +state_features listed in its `feature_input_ports ` attribute. By default, these are the current `input ` for the Composition to which the OptimizationControlMechanism belongs. However, different values can be specified, as can a `state_feature_function ` that transforms these (see `above `). For OptimizationControlMechanisms that -implement `model-free ` optimization, its `state_feature_value +implement `model-free ` optimization, its `state_feature_values ` are used by its `evaluate_agent_rep ` method to predict the `net_outcome ` for a given `control_allocation `. For OptimizationControlMechanisms that @@ -613,7 +613,7 @@ class OptimizationControlMechanism(ControlMechanism): initial_seed : int : default None specifies the seed used to initialize the random number generator at construction. - If it is not specified then then the seed is set to a random value on construction (see `initial_seed + If it is not specified then then the seed is set to a random value (see `initial_seed ` for additional information). same_seed_for_all_parameter_combinations : bool : default False @@ -690,7 +690,7 @@ class OptimizationControlMechanism(ControlMechanism): initial_seed : int or None determines the seed used to initialize the random number generator at construction. - If it is not specified then then the seed is set to a random value on construction, and different runs of a + If it is not specified then then the seed is set to a random value, and different runs of a Composition containing the OptimizationControlMechanism will yield different results, which should be roughly comparable if the estimation process is stable. If **initial_seed** is specified, then running the Composition should yield identical results for the estimation process, which can be useful for debugging. @@ -772,6 +772,8 @@ class OptimizationControlMechanism(ControlMechanism): ` attribute. Each sends a `ControlProjection` to the `ParameterPort` for the Parameter it controls when evaluating a `control_allocation `. + .. _OptimizationControlMechanism_Randomization: + .. technical_note:: If `num_estimates ` is specified (that is, it is not None), a `ControlSignal` is added to control_signals, named *RANDOMIZATION_CONTROL_SIGNAL*, to modulate the @@ -786,25 +788,22 @@ class OptimizationControlMechanism(ControlMechanism): ` of the other ControlSignals (i.e., the ones for the parameters being optimized). The *RANDOMIZATION_CONTROL_SIGNAL* is included when constructing the `control_allocation_search_space ` passed to the - OptimizationControlMechanism's `function ` as its - `search_space `, along with the index of - the *RANDOMIZATION_CONTROL_SIGNAL* as its `randomization_dimension <> - - - - The `initial_seed ` and + OptimizationControlMechanism's `function ` constructor as its + **search_space** argument, along with the index of the *RANDOMIZATION_CONTROL_SIGNAL* as its + **randomization_dimension** argument. The `initial_seed ` and `same_seed_for_all_allocations ` Parameters can be used to further refine this behavior. control_allocation_search_space : list of SampleIterators - `search_space ` assigned by default to `function - `, that determines the samples of - `control_allocation ` evaluated by the `evaluate_agent_rep + `search_space ` assigned by default to the + OptimizationControlMechanism's `function `, that determines the + samples of `control_allocation ` evaluated by the `evaluate_agent_rep ` method. This is a proprety that, unless overridden, returns a list of the `SampleIterators ` generated from the `allocation_sample ` specifications for each of the OptimizationControlMechanism's - `control_signals `. - # FIX: 11/3/21 ADD MENTION OF RANDOMIZATION CONTROL SIGNAL AND RAND DIM PASSED TO OPTIMIZAITON FUNCTION + `control_signals `, and includes the + *RANDOMIZATION_CONTROL_SIGNAL* used to randomize estimates of each `control_allocation + ` (see `note ` above). saved_samples : list contains all values of `control_allocation ` sampled by `function @@ -1068,6 +1067,7 @@ def _instantiate_input_ports(self, context=None): # Assign to self.feature_input_ports start = self.num_outcome_input_ports # FIX: 11/3/21 NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED stop = start + len(feature_input_ports) if feature_input_ports else 0 + # FIX 11/3/21: THIS SHOULD BE MADE A PARAMETER self.feature_input_ports = ContentAddressableList(component_type=InputPort, list=self.input_ports[start:stop]) @@ -1113,11 +1113,16 @@ def _instantiate_control_signals(self, context): if self.num_estimates: # Construct iterator for seeds used to randomize estimates - def random_integer_generator(): - rng = np.random.RandomState() - rng.seed(self.initial_seed) - return rng.random_integers(self.num_estimates) - random_seed_modulation_values = SampleSpec(num=self.num_estimates, function=random_integer_generator) + # if seed_randomization: + # def random_integer_generator(): + # rng = np.random.RandomState() + # rng.seed(self.initial_seed) + # return rng.random_integers(self.num_estimates) + # randomization_seed_mod_values = SampleSpec(num=self.num_estimates, function=random_integer_generator) + # else: + # randomization_seed_mod_values = SampleSpec(start=1,stop=self.num_estimates,step=1) + + randomization_seed_mod_values = SampleSpec(start=1,stop=self.num_estimates,step=1) # FIX: noise PARAM OF TransferMechanism IS MARKED AS SEED WHEN ASSIGNED A DISTRIBUTION FUNCTION, # BUT IT HAS NO PARAMETER PORT BECAUSE THAT PRESUMABLY IS FOR THE INTEGRATOR FUNCTION, @@ -1128,7 +1133,7 @@ def random_integer_generator(): # Construct ControlSignal to modify seeds over estimates self.output_ports.append(ControlSignal(name=RANDOMIZATION_CONTROL_SIGNAL, modulates=seed_param_ports, - allocation_samples=random_seed_modulation_values)) + allocation_samples=randomization_seed_mod_values)) for i, spec in enumerate(self.output_ports): control_signal = self._instantiate_control_signal(spec, context=context) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 91a12311d24..6a2d6962879 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -3134,8 +3134,8 @@ class Composition(Composition_Base, metaclass=ComponentsMeta): single call to `run `. shadows : dict - a dictionary in which the keys are all in the Composition and the values are lists of any Nodes that - `shadow ` the original Node's input. + a dictionary in which the keys are all `Nodes ` in the Composition, + and the values of each is a list of any Nodes that `shadow ` it's input. controller : OptimizationControlMechanism identifies the `OptimizationControlMechanism` used as the Composition's controller @@ -7146,6 +7146,7 @@ def add_controller(self, controller:ControlMechanism, context=None): self.remove_projection(proj) self.controller.composition=None + # Assign mutual references between Composition and controller controller.composition = self self.controller = controller # Having controller in nodes is not currently supported (due to special handling of scheduling/execution); @@ -7159,6 +7160,14 @@ def add_controller(self, controller:ControlMechanism, context=None): if invalid_aux_components: self._controller_initialization_status = ContextFlags.DEFERRED_INIT + # If the controller doesn't have any feature_input_ports, add ones from Composition's INPUT Nodes + if not controller.feature_input_ports: + # FIX: 11/3/21: NEED TO ENSURE THAT ADDED INPUTPORTS HAVE shadow_inputs ATTRIBUTE SET TO True + shadow_inputs = controller._parse_state_feature_specs(self.get_nodes_by_role(NodeRole.INPUT), + None, context=context) + controller.add_ports(shadow_inputs, update_variable=False, context=context) + controller.feature_input_ports.append(shadow_inputs) + # FIX: 11/3/21: ISN'T THIS HANDLED IN HANDLING OF aux_components? if self.controller.objective_mechanism and self.controller.objective_mechanism not in invalid_aux_components: self.add_node(self.controller.objective_mechanism, required_roles=NodeRole.CONTROLLER_OBJECTIVE) @@ -7174,14 +7183,15 @@ def add_controller(self, controller:ControlMechanism, context=None): self._update_shadows_dict(controller) # INSTANTIATE SHADOW_INPUT PROJECTIONS - # Skip controller's first (OUTCOME) input_port (that receives the Projection from its objective_mechanism nested_cims = [comp.input_CIM for comp in self._get_nested_compositions()] input_cims= [self.input_CIM] + nested_cims # For the rest of the controller's input_ports if they are marked as receiving SHADOW_INPUTS, # instantiate the shadowing Projection to them from the sender to the shadowed InputPort # FIX: 11/3/21: BELOW NEEDS TO BE CORRECTED IF OUTCOME InputPort GETS MOVED - # ALSO, IF Non-OCM IS USED AS CONTROLLER, MAY HAVE MORE THAN ONE Inport FOR MONITORING - for input_port in controller.input_ports[1:]: + # ALSO, IF Non-OCM IS ALLOWED AS CONTROLLER, MAY HAVE MORE THAN ONE Inport FOR MONITORING + # Skip controller's outcome_input_ports + # (that receive Projections from its objective_mechanism and/or directed from items in monitor_for_control + for input_port in controller.input_ports[controller.num_outcome_input_ports:]: if hasattr(input_port, SHADOW_INPUTS) and input_port.shadow_inputs is not None: for proj in input_port.shadow_inputs.path_afferents: try: @@ -7508,8 +7518,8 @@ def evaluate( independently sampled seed for the random number generator. All values are reset to pre-simulation values at the end of the simulation. - Returns the `net_outcome ` of a run of - the `agent_rep `. If **return_results** is True, + Returns the `net_outcome ` of a run of the `agent_rep + `. If **return_results** is True, an array with the results of each run is also returned. """ diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 43c45154fb8..cef08a1ba6c 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -430,6 +430,7 @@ def test_hanging_control_spec_nearest_controller(self): assert result == [[5]] assert internal_mech.mod_afferents[0].sender.owner == inner_comp.controller + class TestControlMechanisms: def test_modulation_of_control_signal_intensity_cost_function_MULTIPLICATIVE(self): diff --git a/tests/composition/test_parameterestimationcomposition.py b/tests/composition/test_parameterestimationcomposition.py index cd512203719..e2701c78c02 100644 --- a/tests/composition/test_parameterestimationcomposition.py +++ b/tests/composition/test_parameterestimationcomposition.py @@ -98,10 +98,6 @@ def test_parameter_estimation_composition(objective_function_arg, expected_input ) ctlr = pec.controller - assert comp.controller.num_feature_input_ports == 2 - assert comp.controller.feature_input_ports[0].function.name == pnl.AdaptiveIntegrator.__class__.__name__ - assert comp.controller.feature_input_ports[1].function.name == pnl.AdaptiveIntegrator.__class__.__name__ - assert ctlr.num_outcome_input_ports == 1 if objective_function_arg: assert ctlr.objective_mechanism # For objective_function specified From 7acdebdb5d2818e305e19b73a9ff125a21013d1f Mon Sep 17 00:00:00 2001 From: jdc Date: Tue, 9 Nov 2021 11:09:15 -0500 Subject: [PATCH 049/285] =?UTF-8?q?=E2=80=A2=20composition.py:=20=20=20-?= =?UTF-8?q?=20add=5Fcontroller:=20=20now=20adds=20feature=5Finput=5Fports?= =?UTF-8?q?=20for=20Compostion=20INPUT=20nodes=20if=20not=20state=5Ffeatur?= =?UTF-8?q?es=20not=20specified?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- psyneulink/core/compositions/composition.py | 16 ++++++++++------ tests/composition/test_control.py | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 6a2d6962879..6d5302b2444 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -7162,11 +7162,15 @@ def add_controller(self, controller:ControlMechanism, context=None): # If the controller doesn't have any feature_input_ports, add ones from Composition's INPUT Nodes if not controller.feature_input_ports: - # FIX: 11/3/21: NEED TO ENSURE THAT ADDED INPUTPORTS HAVE shadow_inputs ATTRIBUTE SET TO True - shadow_inputs = controller._parse_state_feature_specs(self.get_nodes_by_role(NodeRole.INPUT), - None, context=context) - controller.add_ports(shadow_inputs, update_variable=False, context=context) - controller.feature_input_ports.append(shadow_inputs) + input_nodes = self.get_nodes_by_role(NodeRole.INPUT) + for node in input_nodes: + # FIX: 11/3/21 NEED TO DEAL WITH NESTED COMP AS INPUT NODE [MAKE METHOD THAT DOES ALL THIS] + feature_input_ports = [] + for input_port in [input_port for input_port in node.input_ports if not input_port.internal_only]: + feature_input_ports.append(input_port) + # controller._parse_state_feature_specs(controller, feature_input_ports) + controller.add_ports(feature_input_ports, update_variable=False, context=context) + controller.feature_input_ports.append(feature_input_ports) # FIX: 11/3/21: ISN'T THIS HANDLED IN HANDLING OF aux_components? if self.controller.objective_mechanism and self.controller.objective_mechanism not in invalid_aux_components: @@ -7248,7 +7252,7 @@ def add_controller(self, controller:ControlMechanism, context=None): ctl_signal = controller._instantiate_control_signal(control_signal=ctl_sig_spec, context=context) controller.control.append(ctl_signal) - # FIX: 9/15/19 - WHAT IF NODE THAT RECEIVES ControlProjection IS NOT YET IN COMPOSITON: + # FIX: 9/15/19 - WHAT IF NODE THAT RECEIVES ControlProjection IS NOT YET IN COMPOSITION: # ?DON'T ASSIGN ControlProjection? # ?JUST DON'T ACTIVATE IT FOR COMPOSITON? # ?PUT IT IN aux_components FOR NODE? diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index cef08a1ba6c..23c9985215b 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1073,6 +1073,7 @@ def test_modulation_simple(self, cost, expected, exp_values): comp.add_controller( pnl.OptimizationControlMechanism( objective_mechanism=obj, + # state_features=mech.input_port, control_signals=pnl.ControlSignal( modulates=('intercept', mech), modulation=pnl.OVERRIDE, From f81baf5f326540433608f21307fb9c49376b1190 Mon Sep 17 00:00:00 2001 From: jdc Date: Tue, 9 Nov 2021 11:11:42 -0500 Subject: [PATCH 050/285] - --- tests/composition/test_control.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 23c9985215b..a89d30ad701 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1052,6 +1052,7 @@ def test_control_of_mech_port(self, comp_mode): @pytest.mark.control @pytest.mark.composition @pytest.mark.parametrize("cost, expected, exp_values", [ + # FIX: 11/3/21: NEED TO CHANGE expected (and exp_values?) NOW THAT feature_input_ports IS IMPLEMENTED (pnl.CostFunctions.NONE, 7.0, [1, 2, 3, 4, 5]), (pnl.CostFunctions.INTENSITY, 3, [-1.71828183, -5.3890561, -17.08553692, -50.59815003, -143.4131591]), (pnl.CostFunctions.ADJUSTMENT, 3, [1, 1, 1, 1, 1] ), From 351458eef55239d9d4d15eab0522aceafb5bce73 Mon Sep 17 00:00:00 2001 From: jdc Date: Tue, 9 Nov 2021 15:59:08 -0500 Subject: [PATCH 051/285] - --- .../nonstateful/optimizationfunctions.py | 19 +++++++++++-------- tests/composition/test_control.py | 1 - 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 024198a230b..737904f3b0a 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -615,6 +615,11 @@ def _function(self, _progress_bar_count = 0 # Iterate optimization process + REPLACE THE WHILE LOOP WITH search_space_evalute() THAT USES num_estimates: \ + BRANCH ON generic or _grid_evalue; RETURN ARRAY OF RESULTS + BASED ON LOGIC OF GRID: IF EVERY DIMENSION IS SimpleIterator (I.E., STATIC) + PUT IN search_space_evalute: IF SAME_SEED, BREAK UP GRID INTO ONES FOR EACH SEEDED SET + while not call_with_pruned_args(self.search_termination_function, current_sample, current_value, iteration, @@ -629,13 +634,8 @@ def _function(self, new_sample = call_with_pruned_args(self.search_function, current_sample, iteration, context=context) # Generate num_estimates of sample, then apply aggregation_function and return result - estimates = [] - num_estimates = self.num_estimates - for i in range(num_estimates): - estimate = call_with_pruned_args(self.objective_function, new_sample, context=context) - estimates.append(estimate) - new_value = self.aggregation_function(estimates, num_estimates) if self.aggregation_function else estimates - self._report_value(new_value) + estimate = call_with_pruned_args(self.objective_function, new_sample, context=context) + self._report_value(estimate) iteration += 1 max_iterations = self.parameters.max_iterations._get(context) if max_iterations and iteration > max_iterations: @@ -652,6 +652,9 @@ def _function(self, values.append(current_value) self.parameters.saved_values._set(values, context) + CALL AGGREGRATE_FUNCTION WITH ARRAY RETURNED BY search_space_evalute AND RETURN PROCESSED ARRAY OF RESULTS + new_value = self.aggregation_function(estimates, num_estimates) if self.aggregation_function else estimates + return new_sample, new_value, samples, values def _report_value(self, new_value): @@ -659,7 +662,7 @@ def _report_value(self, new_value): pass -class GridBasedOptimizationFunction(OptimizationFunction): +class (OptimizationFunction): """Implement helper method for parallelizing instantiation for evaluating samples from search space.""" def _grid_evaluate(self, ocm, context): diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index a89d30ad701..8e2a85270dd 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1074,7 +1074,6 @@ def test_modulation_simple(self, cost, expected, exp_values): comp.add_controller( pnl.OptimizationControlMechanism( objective_mechanism=obj, - # state_features=mech.input_port, control_signals=pnl.ControlSignal( modulates=('intercept', mech), modulation=pnl.OVERRIDE, From 260fb53ecdd29b3940f53be81431dc4922b875b6 Mon Sep 17 00:00:00 2001 From: jdc Date: Tue, 9 Nov 2021 17:02:48 -0500 Subject: [PATCH 052/285] - --- .../nonstateful/optimizationfunctions.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 737904f3b0a..7d5019734fe 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -620,11 +620,20 @@ def _function(self, BASED ON LOGIC OF GRID: IF EVERY DIMENSION IS SimpleIterator (I.E., STATIC) PUT IN search_space_evalute: IF SAME_SEED, BREAK UP GRID INTO ONES FOR EACH SEEDED SET + self.search_space_evalute() + + CALL AGGREGRATE_FUNCTION WITH ARRAY RETURNED BY search_space_evalute AND RETURN PROCESSED ARRAY OF RESULTS + new_value = self.aggregation_function(estimates, num_estimates) if self.aggregation_function else estimates + + return new_sample, new_value, samples, values + + def search_space_evaluate() + while not call_with_pruned_args(self.search_termination_function, current_sample, current_value, iteration, context=context): - if _show_progress: + if _show_progress: increment_progress_bar = (_progress_bar_rate < 1) or not (_progress_bar_count % _progress_bar_rate) if increment_progress_bar: print(_progress_bar_char, end='', flush=True) @@ -652,10 +661,6 @@ def _function(self, values.append(current_value) self.parameters.saved_values._set(values, context) - CALL AGGREGRATE_FUNCTION WITH ARRAY RETURNED BY search_space_evalute AND RETURN PROCESSED ARRAY OF RESULTS - new_value = self.aggregation_function(estimates, num_estimates) if self.aggregation_function else estimates - - return new_sample, new_value, samples, values def _report_value(self, new_value): """Report value returned by `objective_function ` for sample.""" From 07ea9b87134a6362cdab8f50b81d9904a04651e0 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Tue, 9 Nov 2021 20:57:44 -0500 Subject: [PATCH 053/285] =?UTF-8?q?=E2=80=A2=20optimizationfunctions.py:?= =?UTF-8?q?=20=20=20=5F=5Finit=5F=5F:=20move=20randomization=20axis=20to?= =?UTF-8?q?=20last=20one=20=20=20=5Ffunction:=20refactor=20to=20use=20=5Fg?= =?UTF-8?q?rid=5Fevaluate=20or=20=5Fsequential=5Fevaluate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonstateful/optimizationfunctions.py | 86 ++++++++++--------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 7d5019734fe..cb2a5f2d889 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -587,18 +587,35 @@ def _function(self, assert all([not getattr(self.parameters, x)._user_specified for x in self._unspecified_args]) self._unspecified_args = [] - current_sample = self._check_args(variable=variable, context=context, params=params) - + # Get initial sample in case it is needed by _search_space_evaluate (e.g., for gradient initialization) + initial_sample = self._check_args(variable=variable, context=context, params=params) try: - current_value = self.owner.objective_mechanism.parameters.value._get(context) + initial_value = self.owner.objective_mechanism.parameters.value._get(context) except AttributeError: - current_value = 0 + initial_value = 0 + + # EVALUATE ALL SAMPLES IN SEARCH SPACE + # Evaluate all estimates of all samples in search_space - samples = [] - values = [] + # Grid is static, so use parallelized evaluation + if all(isinstance(sample_iterator.start, int) and isinstance(sample_iterator.stop, int) + for sample_iterator in self.search_space): + return self._grid_evaluate(self.owner, context) + # Use default sequential sampling + else: + return self._default_search_space_evaluate(initial_sample, initial_value, context) + + def _default_search_space_evaluate(self, initial_sample, initial_value, context): + """Sequentially evaluate every sample in search_space. + Return last sample, last value, arrays with all samples evaluated, and array with all values of those samples. + """ # Initialize variables used in while loop iteration = 0 + current_sample = initial_sample + current_value = initial_value + all_samples = [] + all_values = [] # Set up progress bar _show_progress = False @@ -613,53 +630,45 @@ def _function(self, print("\n{} executing optimization process (one {} for each {}of {} samples): ". format(self.owner.name, repr(_progress_bar_char), _progress_bar_rate_str, _search_space_size)) _progress_bar_count = 0 - # Iterate optimization process - - REPLACE THE WHILE LOOP WITH search_space_evalute() THAT USES num_estimates: \ - BRANCH ON generic or _grid_evalue; RETURN ARRAY OF RESULTS - BASED ON LOGIC OF GRID: IF EVERY DIMENSION IS SimpleIterator (I.E., STATIC) - PUT IN search_space_evalute: IF SAME_SEED, BREAK UP GRID INTO ONES FOR EACH SEEDED SET - - self.search_space_evalute() - - CALL AGGREGRATE_FUNCTION WITH ARRAY RETURNED BY search_space_evalute AND RETURN PROCESSED ARRAY OF RESULTS - new_value = self.aggregation_function(estimates, num_estimates) if self.aggregation_function else estimates - - return new_sample, new_value, samples, values - - def search_space_evaluate() + # Iterate over samples until search_termination_function returns True while not call_with_pruned_args(self.search_termination_function, current_sample, current_value, iteration, context=context): - if _show_progress: + if _show_progress: increment_progress_bar = (_progress_bar_rate < 1) or not (_progress_bar_count % _progress_bar_rate) if increment_progress_bar: print(_progress_bar_char, end='', flush=True) _progress_bar_count +=1 # Get next sample of sample - new_sample = call_with_pruned_args(self.search_function, current_sample, iteration, context=context) - - # Generate num_estimates of sample, then apply aggregation_function and return result - estimate = call_with_pruned_args(self.objective_function, new_sample, context=context) - self._report_value(estimate) + current_sample = call_with_pruned_args(self.search_function, current_sample, iteration, context=context) + current_value = call_with_pruned_args(self.objective_function, sample, context=context) + self._report_value(current_value) iteration += 1 max_iterations = self.parameters.max_iterations._get(context) if max_iterations and iteration > max_iterations: warnings.warn("{} failed to converge after {} iterations".format(self.name, max_iterations)) break - current_sample = new_sample - current_value = new_value + all_values.append(current_value) + + # FIX: 11/3/21 CULL SAMPLES TO ONLY ONE OF EACH NON-SEED VALUE (OR GENERATE DE NOVE USING iter.tools) + all_samples = XXX + last_sample = current_sample + aggregated_values = self._aggregrate_values(all_values) + last_value = aggregated_values[-1] + + if self.parameters.save_samples._get(context): + self.parameters.saved_samples._set(all_samples, context) + if self.parameters.save_values._get(context): + self.parameters.saved_values._set(aggregated_values, context) - if self.parameters.save_samples._get(context): - samples.append(new_sample) - self.parameters.saved_samples._set(samples, context) - if self.parameters.save_values._get(context): - values.append(current_value) - self.parameters.saved_values._set(values, context) + return last_sample, last_value, all_samples, aggregated_values + + def _aggregate_values(self, all_values): + # FIX: 11/3/21 GET SEED DIMENSION AND COLLAPSE ALONG THAT USEING self.agregation_function def _report_value(self, new_value): @@ -667,7 +676,7 @@ def _report_value(self, new_value): pass -class (OptimizationFunction): +class GridBasedOptimizationFunction(OptimizationFunction): """Implement helper method for parallelizing instantiation for evaluating samples from search space.""" def _grid_evaluate(self, ocm, context): @@ -1233,7 +1242,7 @@ def _convergence_condition(self, variable, value, iteration, context=None): MINIMIZE = 'minimize' -class GridSearch(GridBasedOptimizationFunction): +class GridSearch(OptimizationFunction): """ GridSearch( \ default_variable=None, \ @@ -1624,7 +1633,7 @@ def _gen_llvm_select_min_function(self, *, ctx:pnlvm.LLVMBuilderContext, tags:fr with b_true: search_space = pnlvm.helpers.get_param_ptr(builder, self, params, self.parameters.search_space.name) - pnlvm.helpers.create_allocation(b, min_sample_ptr, search_space, min_idx) + pnlvm.helpers.create_sample(b, min_sample_ptr, search_space, min_idx) with b_false: sample_ptr = builder.gep(samples_ptr, [min_idx]) builder.store(b.load(sample_ptr), min_sample_ptr) @@ -1860,7 +1869,6 @@ def _function(self, "PROGRAM ERROR: bad value for {} arg of {}: {}, {}". \ format(repr(DIRECTION), self.name, direction) - ocm = self._get_optimized_controller() # Compiled version From e1a7598cc824969abb4191a02821b7b3a54dbdd6 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Tue, 9 Nov 2021 21:03:58 -0500 Subject: [PATCH 054/285] =?UTF-8?q?=E2=80=A2=20optimizationfunctions.py:?= =?UTF-8?q?=20=20=20=5F=5Finit=5F=5F:=20move=20randomization=20to=20last?= =?UTF-8?q?=20dimension=20=20=20=5Ffunction:=20refactor=20to=20use=20=5Fgr?= =?UTF-8?q?id=5Fevaluate=20or=20=5Fsequential=5Fevaluate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../functions/nonstateful/optimizationfunctions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index cb2a5f2d889..6891cda95c7 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -432,6 +432,8 @@ def __init__( self._unspecified_args.append(SEARCH_TERMINATION_FUNCTION) self.randomization_dimension = randomization_dimension + # Max randomization dimension of search_space last for standardization of treatment + self.search_space.append(self.search_space.pop(self.search_space.index(self.randomization_dimension))) super().__init__( default_variable=default_variable, @@ -603,9 +605,9 @@ def _function(self, return self._grid_evaluate(self.owner, context) # Use default sequential sampling else: - return self._default_search_space_evaluate(initial_sample, initial_value, context) + return self._sequential_evaluate(initial_sample, initial_value, context) - def _default_search_space_evaluate(self, initial_sample, initial_value, context): + def _sequential_evaluate(self, initial_sample, initial_value, context): """Sequentially evaluate every sample in search_space. Return last sample, last value, arrays with all samples evaluated, and array with all values of those samples. """ From 8b6ca09c32d76463ca8947ce955b07fd57f63a26 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Tue, 9 Nov 2021 23:07:05 -0500 Subject: [PATCH 055/285] - --- .../nonstateful/optimizationfunctions.py | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 6891cda95c7..9f5a131bd1f 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -432,8 +432,9 @@ def __init__( self._unspecified_args.append(SEARCH_TERMINATION_FUNCTION) self.randomization_dimension = randomization_dimension - # Max randomization dimension of search_space last for standardization of treatment - self.search_space.append(self.search_space.pop(self.search_space.index(self.randomization_dimension))) + if self.search_space: + # Make randomization dimension of search_space last for standardization of treatment + self.search_space.append(self.search_space.pop(self.search_space.index(self.randomization_dimension))) super().__init__( default_variable=default_variable, @@ -644,34 +645,39 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): print(_progress_bar_char, end='', flush=True) _progress_bar_count +=1 - # Get next sample of sample - current_sample = call_with_pruned_args(self.search_function, current_sample, iteration, context=context) - current_value = call_with_pruned_args(self.objective_function, sample, context=context) + # Assumes randomizaton dimension is last dimension (set in __init__), with length num_estimates + # so should be able to loop over it for num_estimates + # aggregation + estimates_for_sample = [] + for i in range(self.num_estimates): + # Get next sample + current_sample = call_with_pruned_args(self.search_function, current_sample, iteration, context=context) + # Get value of sample + estimate = call_with_pruned_args(self.objective_function, current_sample, context=context) + estimates_for_sample.append(estimate) + + # Get aggregated value of num_estimates for sample + aggregated_value = (self.aggregration_function(estimates_for_sample, self.num_estimates)) + + all_values.append(aggregated_value) + all_samples.append(current_sample) + self._report_value(current_value) iteration += 1 max_iterations = self.parameters.max_iterations._get(context) if max_iterations and iteration > max_iterations: - warnings.warn("{} failed to converge after {} iterations".format(self.name, max_iterations)) + warnings.warn(f"{self.name} of {self.owner.name} exceeded max iterations {max_iterations}.") break - all_values.append(current_value) - - # FIX: 11/3/21 CULL SAMPLES TO ONLY ONE OF EACH NON-SEED VALUE (OR GENERATE DE NOVE USING iter.tools) - all_samples = XXX last_sample = current_sample - aggregated_values = self._aggregrate_values(all_values) - last_value = aggregated_values[-1] + last_value = aggregated_value if self.parameters.save_samples._get(context): self.parameters.saved_samples._set(all_samples, context) if self.parameters.save_values._get(context): - self.parameters.saved_values._set(aggregated_values, context) - - return last_sample, last_value, all_samples, aggregated_values - - def _aggregate_values(self, all_values): - # FIX: 11/3/21 GET SEED DIMENSION AND COLLAPSE ALONG THAT USEING self.agregation_function + self.parameters.saved_values._set(all_values, context) + return last_sample, last_value, all_samples, all_values def _report_value(self, new_value): """Report value returned by `objective_function ` for sample.""" From 699d082f19652510e0f53d1166f4e82547f803dc Mon Sep 17 00:00:00 2001 From: jdcpni Date: Tue, 9 Nov 2021 23:27:15 -0500 Subject: [PATCH 056/285] =?UTF-8?q?=E2=80=A2=20optimizationfunctions.py:?= =?UTF-8?q?=20=20=20=20=201)=20Got=20rid=20of=20GridBasedOptimizationFunct?= =?UTF-8?q?ion,=20and=20put=20=5Fgrid=5Fevaluate()=20on=20the=20base=20cla?= =?UTF-8?q?ss=20=20=20=20=202)=20Added=20a=20new=20=E2=80=9Csibling?= =?UTF-8?q?=E2=80=9D=20method,=20called=20=5Fsequential=5Fevaluate(),=20wh?= =?UTF-8?q?ich=20has=20the=20while=20loop=20from=20=5Ffunction=20=20=20=20?= =?UTF-8?q?=203)=20Put=20the=20test=20for=20whether=20to=20use=20grid=20or?= =?UTF-8?q?=20sequential=20in=20=5Ffunction()=20=20=20=20=204)=20Enforced?= =?UTF-8?q?=20that=20randomization=20dimension=20is=20last=20(in=20=5Finit?= =?UTF-8?q?=5F())=20=20=20=20=205)=20Restored=20the=20loop=20on=20num=5Fes?= =?UTF-8?q?timates=20w/in=20=5Fsequential=5Fevaluate,=20=20so=20that=20it?= =?UTF-8?q?=20can=20aggregate=20over=20those=20progressively,=20assuming?= =?UTF-8?q?=20that=20randomization=20dimension=20is=20last?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonstateful/optimizationfunctions.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 9f5a131bd1f..909b60d8530 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -584,6 +584,7 @@ def _function(self, second list contains the values returned by `objective_function ` for all the samples in the order they were evaluated; otherwise it is empty. """ + if self._unspecified_args and self.initialization_status == ContextFlags.INITIALIZED: warnings.warn("The following arg(s) were not specified for {}: {} -- using default(s)". format(self.name, ', '.join(self._unspecified_args))) @@ -600,11 +601,12 @@ def _function(self, # EVALUATE ALL SAMPLES IN SEARCH SPACE # Evaluate all estimates of all samples in search_space - # Grid is static, so use parallelized evaluation - if all(isinstance(sample_iterator.start, int) and isinstance(sample_iterator.stop, int) - for sample_iterator in self.search_space): + # If execution mode is not Python and search_space is static, use parallelized evaluation: + if (self.owner.parameters.comp_execution_mode._get(context) != 'Python' and + all(isinstance(sample_iterator.start, int) and isinstance(sample_iterator.stop, int) + for sample_iterator in self.search_space)): return self._grid_evaluate(self.owner, context) - # Use default sequential sampling + # Otherwise, default sequential sampling else: return self._sequential_evaluate(initial_sample, initial_value, context) @@ -657,7 +659,7 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): estimates_for_sample.append(estimate) # Get aggregated value of num_estimates for sample - aggregated_value = (self.aggregration_function(estimates_for_sample, self.num_estimates)) + aggregated_value = (self.aggregation_function(estimates_for_sample, self.num_estimates)) all_values.append(aggregated_value) all_samples.append(current_sample) @@ -679,16 +681,9 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): return last_sample, last_value, all_samples, all_values - def _report_value(self, new_value): - """Report value returned by `objective_function ` for sample.""" - pass - - -class GridBasedOptimizationFunction(OptimizationFunction): - """Implement helper method for parallelizing instantiation for evaluating samples from search space.""" - def _grid_evaluate(self, ocm, context): - + """Helper method for parallelizing evaluation of samples from search space. + """ assert ocm is ocm.agent_rep.controller # Compiled evaluate expects the same variable as mech function variable = [input_port.parameters.value.get(context) for input_port in ocm.input_ports] @@ -706,6 +701,10 @@ def _grid_evaluate(self, ocm, context): return outcomes, num_evals + def _report_value(self, new_value): + """Report value returned by `objective_function ` for sample.""" + pass + ASCENT = 'ascent' DESCENT = 'descent' From 4d6e1aa6aead88575f91611e864bc01350ff9bd8 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 01:29:44 -0500 Subject: [PATCH 057/285] =?UTF-8?q?=E2=80=A2=20optimizationcontrolmechanis?= =?UTF-8?q?m.py,=20parameterestimationcomposition.py:=20=20=20-=20removed?= =?UTF-8?q?=20initial=5Fseed=20=20=20-=20same=5Fseed=5Ffor=5Fall=5F<*>=20-?= =?UTF-8?q?>=20same=5Frandomization=5Ffor=5Fall=5F<*>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../control/optimizationcontrolmechanism.py | 100 +++++++----------- .../parameterestimationcomposition.py | 65 +++--------- 2 files changed, 53 insertions(+), 112 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index f2d6829e2d2..8205f78c9b1 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -372,9 +372,8 @@ `control_allocation ` is independently evaluated `num_estimates ` times (i.e., by that number of calls to the OptimizationControlMechanism's `evaluate_agent_rep ` method. Randomization over -estimates can be configured using the OptimizationControlMechanism's `initial_seed -` and `same_seed_for_all_allocations -` Parameters; see `control_signals +estimates can be configured using the OptimizationControlMechanism's `same_randomization_for_all_allocations +` Parameter; see `control_signals ` for additional information. The results of the independent estimates are aggregated by the `aggregation_function ` of the `OptimizationFunction` assigned to the OptimizationControlMechanism's `function `, @@ -557,26 +556,25 @@ def _control_allocation_search_space_getter(owning_component=None, context=None) class OptimizationControlMechanism(ControlMechanism): - """OptimizationControlMechanism( \ - objective_mechanism=None, \ - monitor_for_control=None, \ - origin_objective_mechanism=False \ - terminal_objective_mechanism=False \ - state_features=None, \ - state_feature_function=None, \ - function=GridSearch, \ - agent_rep=None, \ - num_estimates=1, \ - initial_seed=None, \ - same_seed_for_all_parameter_combinations=False \ - num_trials_per_estimate=None, \ - search_function=None, \ - search_termination_function=None, \ - search_space=None, \ - control_signals=None, \ - modulation=MULTIPLICATIVE, \ - combine_costs=np.sum, \ - compute_reconfiguration_cost=None, \ + """OptimizationControlMechanism( \ + objective_mechanism=None, \ + monitor_for_control=None, \ + origin_objective_mechanism=False \ + terminal_objective_mechanism=False \ + state_features=None, \ + state_feature_function=None, \ + function=GridSearch, \ + agent_rep=None, \ + num_estimates=1, \ + same_randomization_for_all_parameter_combinations=True \ + num_trials_per_estimate=None, \ + search_function=None, \ + search_termination_function=None, \ + search_space=None, \ + control_signals=None, \ + modulation=MULTIPLICATIVE, \ + combine_costs=np.sum, \ + compute_reconfiguration_cost=None, \ compute_net_outcome=lambda x,y:x-y) Subclass of `ControlMechanism ` that adjusts its `ControlSignals ` to optimize @@ -611,15 +609,11 @@ class OptimizationControlMechanism(ControlMechanism): ` sampled (see `num_estimates ` for additional information). - initial_seed : int : default None - specifies the seed used to initialize the random number generator at construction. - If it is not specified then then the seed is set to a random value (see `initial_seed - ` for additional information). - - same_seed_for_all_parameter_combinations : bool : default False - specifies whether the random number generator is re-initialized to the same value when estimating each - `control_allocation ` (see `same_seed_for_all_parameter_combinations - ` for additional information). + same_randomization_for_all_parameter_combinations : bool : default False + specifies whether the random number generator is re-initialized to the + same value at the start of estimating each `control_allocation ` + (see `same_randomization_for_all_parameter_combinations + ` for additional information). num_trials_per_estimate : int : default None specifies the number of trials to execute in each run of `agent_rep @@ -688,25 +682,18 @@ class OptimizationControlMechanism(ControlMechanism): that are specified by its `search_space `). # FIX: 11/3/21 ADD POINTER TO DESCRIPTINO OF RAONDIMZATION CONTROL SIGNAL - initial_seed : int or None - determines the seed used to initialize the random number generator at construction. - If it is not specified then then the seed is set to a random value, and different runs of a - Composition containing the OptimizationControlMechanism will yield different results, which should be roughly - comparable if the estimation process is stable. If **initial_seed** is specified, then running the Composition - should yield identical results for the estimation process, which can be useful for debugging. - - same_seed_for_all_allocations : bool + same_randomization_for_all_allocations : bool determines whether the random number generator used to select seeds for each estimate of the `agent_rep `\\'s `net_outcome ` is re-initialized to the same value for each `control_allocation ` evaluated. - If same_seed_for_all_allocations is True, then any differences in the estimates made of `net_outcome - ` for each `control_allocation ` will reflect - exclusively the influence of the different control_allocations on the execution of the `agent_rep + If same_randomization_for_all_allocations is True, then any differences in the estimates made of `net_outcome + ` for each `control_allocation ` will + reflect exclusively the influence of the different control_allocations on the execution of the `agent_rep `, and *not* any variability intrinsic to the execution of the Composition itself (e.g., any of its Components). This can be confirmed by identical results for repeated executions of the OptimizationControlMechanism's `evaluate_agent_rep ` method for the same `control_allocation - `. If same_seed_for_all_allocations is False, then each time a + `. If same_randomization_for_all_allocations is False, then each time a `control_allocation ` is estimated, it will use a different set of seeds. This can be confirmed by differing results for repeated executions of the OptimizationControlMechanism's `evaluate_agent_rep ` method with the same `control_allocation @@ -790,9 +777,9 @@ class OptimizationControlMechanism(ControlMechanism): `control_allocation_search_space ` passed to the OptimizationControlMechanism's `function ` constructor as its **search_space** argument, along with the index of the *RANDOMIZATION_CONTROL_SIGNAL* as its - **randomization_dimension** argument. The `initial_seed ` and - `same_seed_for_all_allocations ` - Parameters can be used to further refine this behavior. + **randomization_dimension** argument. The`same_randomization_for_all_allocations + ` Parameter + can be used to further refine this behavior. control_allocation_search_space : list of SampleIterators `search_space ` assigned by default to the @@ -937,9 +924,7 @@ class Parameters(ControlMechanism.Parameters): user=False, pnl_internal=True) - # FIX: Should any of these be stateful? - initial_seed = None - same_seed_for_all_allocations = False + same_randomization_for_all_allocations = True num_estimates = None num_trials_per_estimate = None @@ -957,8 +942,7 @@ def __init__(self, state_features: tc.optional(tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort))) = None, state_feature_function: tc.optional(tc.optional(tc.any(is_function_type))) = None, num_estimates = None, - initial_seed=None, - same_seed_for_all_allocations=None, + same_randomization_for_all_allocations=None, num_trials_per_estimate = None, search_function: tc.optional(tc.optional(tc.any(is_function_type))) = None, search_termination_function: tc.optional(tc.optional(tc.any(is_function_type))) = None, @@ -1018,8 +1002,7 @@ def __init__(self, state_feature_function=state_feature_function, num_estimates=num_estimates, num_trials_per_estimate = num_trials_per_estimate, - initial_seed=initial_seed, - same_seed_for_all_allocations=same_seed_for_all_allocations, + same_randomization_for_all_allocations=same_randomization_for_all_allocations, search_statefulness=search_statefulness, search_function=search_function, search_termination_function=search_termination_function, @@ -1112,15 +1095,6 @@ def _instantiate_control_signals(self, context): """ if self.num_estimates: - # Construct iterator for seeds used to randomize estimates - # if seed_randomization: - # def random_integer_generator(): - # rng = np.random.RandomState() - # rng.seed(self.initial_seed) - # return rng.random_integers(self.num_estimates) - # randomization_seed_mod_values = SampleSpec(num=self.num_estimates, function=random_integer_generator) - # else: - # randomization_seed_mod_values = SampleSpec(start=1,stop=self.num_estimates,step=1) randomization_seed_mod_values = SampleSpec(start=1,stop=self.num_estimates,step=1) diff --git a/psyneulink/core/compositions/parameterestimationcomposition.py b/psyneulink/core/compositions/parameterestimationcomposition.py index 2c4acfc958c..443a4155474 100644 --- a/psyneulink/core/compositions/parameterestimationcomposition.py +++ b/psyneulink/core/compositions/parameterestimationcomposition.py @@ -167,24 +167,14 @@ def __init__(self, error_value): self.error_value = error_value -def _initial_seed_getter(owning_component, context=None): - try: - return owning_component.controler.parameters.initial_seed._get(context) - except: - return None - -def _initial_seed_setter(value, owning_component, context=None): - owning_component.controler.parameters.initial_seed.set(value, context) - return value - def _same_seed_for_all_parameter_combinations_getter(owning_component, context=None): try: - return owning_component.controler.parameters.same_seed_for_all_allocations._get(context) + return owning_component.controler.parameters.same_randomization_for_all_allocations._get(context) except: return None def _same_seed_for_all_parameter_combinations_setter(value, owning_component, context=None): - owning_component.controler.parameters.same_seed_for_all_allocations.set(value, context) + owning_component.controler.parameters.same_randomization_for_all_allocations.set(value, context) return value @@ -199,8 +189,7 @@ class ParameterEstimationComposition(Composition): optimization_function=None, num_estimates=1, number_trials_per_estimate=None, - initial_seed=None, - same_seed_for_all_parameter_combinations=False + same_randomization_for_all_parameter_combinations=True ) Subclass of `Composition` that estimates specified parameters either to fit the results of a Composition @@ -258,7 +247,7 @@ class ParameterEstimationComposition(Composition): specifies the number of estimates made for a each combination of `parameter ` values (see `num_estimates ` for additional information); it is passed to the ParameterEstimationComposition's `controller ` to set its - `num_estimates ` Parameter. + `num_estimates ` Parameter. num_trials_per_estimate : int : default None specifies an exact number of trials to execute for each run of the `model @@ -266,17 +255,12 @@ class ParameterEstimationComposition(Composition): ` values (see `num_trials_per_estimate ` for additional information). - initial_seed : int : default None - specifies the seed used to initialize the random number generator at construction; it is passed to the - ParameterEstimationComposition's `controller ` to set its `initial_seed - ` Parameter. - - same_seed_for_all_parameter_combinations : bool : default False + same_randomization_for_all_parameter_combinations : bool : default False specifies whether the random number generator is re-initialized to the same value when estimating each combination of `parameter ` values; it is passed to the ParameterEstimationComposition's `controller ` to set its - `same_seed_for_all_allocations ` Parameter. - + `same_randomization_for_all_allocations ` + Parameter. Attributes ---------- @@ -357,19 +341,14 @@ class ParameterEstimationComposition(Composition): trials are run for a given combination of `parameter ` values *within* each fit. - initial_seed : int or None - contains the seed used to initialize the random number generator at construction, that is stored on the - ParameterEstimationComposition's `controller `, and setting it sets the value - of that Parameter (see `initial_seed ` for additional details). - - same_seed_for_all_parameter_combinations : bool + same_randomization_for_all_parameter_combinations : bool contains the setting for determining whether the random number generator used to select seeds for each estimate of the `model `\\'s `net_outcome ` is re-initialized to the same value for each combination of `parameter ` values evaluated. Its values is stored on the ParameterEstimationComposition's `controller `, and setting it sets the value - of that Parameter (see `same_seed_for_all_allocations - ` for additional details). + of that Parameter (see `same_randomization_for_all_allocations + ` for additional details). optimized_parameter_values : list contains the values of the `parameters ` of the `model @@ -408,23 +387,15 @@ class Parameters(Composition.Parameters): Attributes ---------- - initial_seed - see `input_specification ` - - :default value: None - :type: ``int`` - - same_seed_for_all_parameter_combinations - see `input_specification ` + same_randomization_for_all_parameter_combinations + see `same_randomization_for_all_parameter_combinations + ` :default value: False :type: ``bool`` """ # FIX: 11/32/21 CORRECT INITIAlIZATIONS? - initial_seed = Parameter(None, loggable=False, pnl_internal=True, - getter=_initial_seed_getter, - setter=_initial_seed_setter) same_seed_for_all_parameter_combinations = Parameter(False, loggable=False, pnl_internal=True, getter=_same_seed_for_all_parameter_combinations_getter, setter=_same_seed_for_all_parameter_combinations_setter) @@ -438,8 +409,7 @@ def __init__(self, objective_function=None, # function of OCM ObjectiveMechanism num_estimates=1, # num seeds per parameter combination (i.e., of OCM allocation_samples) num_trials_per_estimate=None, # num trials per run of model for each combination of parameters - initial_seed=None, - same_seed_for_all_parameter_combinations=False, + same_seed_for_all_parameter_combinations=True, name=None, **kwargs): @@ -470,7 +440,6 @@ def __init__(self, optimization_function=optimization_function, num_estimates=num_estimates, num_trials_per_estimate=num_trials_per_estimate, - initial_seed=initial_seed, same_seed_for_all_parameter_combinations=same_seed_for_all_parameter_combinations) self.add_controller(ocm) @@ -521,7 +490,6 @@ def _instantiate_ocm(self, optimization_function, num_estimates, num_trials_per_estimate, - initial_seed, same_seed_for_all_parameter_combinations ): @@ -547,8 +515,7 @@ def _instantiate_ocm(self, control_signals=control_signals, num_estimates=num_estimates, num_trials_per_estimate=num_trials_per_estimate, - initial_seed=initial_seed, - same_seed_for_all_allocations=same_seed_for_all_parameter_combinations + same_randomization_for_all_allocations=same_seed_for_all_parameter_combinations ) # def run(self): @@ -589,5 +556,5 @@ def _instantiate_ocm(self, # # FIX: THE FOLLOWING MOSTLY NEEDS TO BE HANDLED BY OptimizationFunction.evaluate_agent_rep AND/OR grid_evaluate # # FIX: THIS NEEDS TO BE A DEQUE THAT TRACKS ALL THE CONTROL_SIGNAL VALUES OVER num_estimates FOR PARAM DISTRIB # # FIX: AUGMENT TO USE num_estimates and num_trials_per_estimate - # # FIX: AUGMENT TO USE same_seed_for_all_parameter_combinations PARAMETER + # # FIX: AUGMENT TO USE same_randomization_for_all_parameter_combinations PARAMETER # return self.function(feature_values, control_allocation, context=context) From 114403a0dedccb2cd1b63fb6572f90a4072dc08b Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 01:40:29 -0500 Subject: [PATCH 058/285] =?UTF-8?q?=E2=80=A2=20optimizationfunctions.py=20?= =?UTF-8?q?=20=20-=20=5Fsequential=5Fevaluate:=20implement=20same=5Frandom?= =?UTF-8?q?ization=5Ffor=5Fall=5Fallocations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../functions/nonstateful/optimizationfunctions.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 909b60d8530..a813fda1bd4 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -671,6 +671,11 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): warnings.warn(f"{self.name} of {self.owner.name} exceeded max iterations {max_iterations}.") break + # Change randomization for next sample if specified + if self.owner.parameters.same_randomization_for_all_allocations._get(context) is False: + self.search_space[self.parameters.randomization_dimension._get(context)].start += 1 + self.search_space[self.parameters.randomization_dimension._get(context)].stop += 1 + last_sample = current_sample last_value = aggregated_value From 384860d69b6786728bb7fbfda4037f6115fcdf16 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 01:42:59 -0500 Subject: [PATCH 059/285] - --- .../components/functions/nonstateful/optimizationfunctions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index a813fda1bd4..9ef6f035e5a 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -672,7 +672,7 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): break # Change randomization for next sample if specified - if self.owner.parameters.same_randomization_for_all_allocations._get(context) is False: + if self.owner.parameters.same_randomization_for_all_allocations is False: self.search_space[self.parameters.randomization_dimension._get(context)].start += 1 self.search_space[self.parameters.randomization_dimension._get(context)].stop += 1 From 3b9cdca8fe1c956a79b6a95a08b0810fffa8ca95 Mon Sep 17 00:00:00 2001 From: jdc Date: Wed, 10 Nov 2021 16:02:26 -0500 Subject: [PATCH 060/285] =?UTF-8?q?=E2=80=A2=20optimizationfunctions.py:?= =?UTF-8?q?=20=20=20=5Ffunction:=20re-removed=20num=5Festiamtes=20loop,=20?= =?UTF-8?q?and=20use=20np.apply=20to=20apply=20aggregation=5Ffunction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonstateful/optimizationfunctions.py | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 9ef6f035e5a..855d53dd3b9 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -605,14 +605,27 @@ def _function(self, if (self.owner.parameters.comp_execution_mode._get(context) != 'Python' and all(isinstance(sample_iterator.start, int) and isinstance(sample_iterator.stop, int) for sample_iterator in self.search_space)): - return self._grid_evaluate(self.owner, context) + last_sample, last_value, all_samples, all_values = self._grid_evaluate(self.owner, context) # Otherwise, default sequential sampling else: - return self._sequential_evaluate(initial_sample, initial_value, context) + last_sample, last_value, all_samples, all_values = self._sequential_evaluate(initial_sample, + initial_value, + context) + + # If aggregation_function is specified, use it to aggregate over randomization dimension + if self.aggregation_function is not None: + randomization_dim = self.parameters.randomization_dimension._get(context) + all_values = np.apply_along_axis(self.aggregation_function, + randomization_dim, + all_values, + self.num_estimates) + + # Return list of unique samples and aggregated values over them + return last_sample, last_value, all_samples, all_values def _sequential_evaluate(self, initial_sample, initial_value, context): """Sequentially evaluate every sample in search_space. - Return last sample, last value, arrays with all samples evaluated, and array with all values of those samples. + Return arrays with all samples evaluated, and array with all values of those samples. """ # Initialize variables used in while loop @@ -637,6 +650,8 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): _progress_bar_count = 0 # Iterate over samples until search_termination_function returns True + samples = [] + values = [] while not call_with_pruned_args(self.search_termination_function, current_sample, current_value, iteration, @@ -647,22 +662,13 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): print(_progress_bar_char, end='', flush=True) _progress_bar_count +=1 - # Assumes randomizaton dimension is last dimension (set in __init__), with length num_estimates - # so should be able to loop over it for num_estimates - # aggregation - estimates_for_sample = [] - for i in range(self.num_estimates): - # Get next sample - current_sample = call_with_pruned_args(self.search_function, current_sample, iteration, context=context) - # Get value of sample - estimate = call_with_pruned_args(self.objective_function, current_sample, context=context) - estimates_for_sample.append(estimate) - - # Get aggregated value of num_estimates for sample - aggregated_value = (self.aggregation_function(estimates_for_sample, self.num_estimates)) + # Get next sample + current_sample = call_with_pruned_args(self.search_function, current_sample, iteration, context=context) + # Get value of sample + current_value = call_with_pruned_args(self.objective_function, current_sample, context=context) - all_values.append(aggregated_value) - all_samples.append(current_sample) + samples.append(current_sample) + values.append(current_value) self._report_value(current_value) iteration += 1 @@ -671,20 +677,18 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): warnings.warn(f"{self.name} of {self.owner.name} exceeded max iterations {max_iterations}.") break - # Change randomization for next sample if specified + # Change randomization for next sample if specified (relies on randomization being last dimension) if self.owner.parameters.same_randomization_for_all_allocations is False: self.search_space[self.parameters.randomization_dimension._get(context)].start += 1 self.search_space[self.parameters.randomization_dimension._get(context)].stop += 1 - last_sample = current_sample - last_value = aggregated_value - if self.parameters.save_samples._get(context): self.parameters.saved_samples._set(all_samples, context) if self.parameters.save_values._get(context): self.parameters.saved_values._set(all_values, context) - return last_sample, last_value, all_samples, all_values + # FIX: 11/3/21: ??MODIFY TO RETURN SAME AS _grid_evaluate + return current_sample, current_value, samples, values def _grid_evaluate(self, ocm, context): """Helper method for parallelizing evaluation of samples from search space. From 48ffca12cc27912e0df2dccb0d9e0792871ac03c Mon Sep 17 00:00:00 2001 From: jdc Date: Wed, 10 Nov 2021 17:17:11 -0500 Subject: [PATCH 061/285] =?UTF-8?q?=E2=80=A2=20optimizationfunctions.py:?= =?UTF-8?q?=20=20=20-=20=5Ffunction:=20refactored=20to=20put=20use=20aggre?= =?UTF-8?q?gation=5Ffunction=20at=20end=20=20=20-=20=5Fgrid=5Fevaluate:=20?= =?UTF-8?q?=20still=20needs=20to=20return=20all=5Fsamples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonstateful/optimizationfunctions.py | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 855d53dd3b9..ec9a3dd4387 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -379,7 +379,7 @@ class Parameters(Function_Base.Parameters): variable = Parameter(np.array([0, 0, 0]), read_only=True, pnl_internal=True, constructor_argument='default_variable') objective_function = Parameter(lambda x: 0, stateful=False, loggable=False) - aggregation_function = Parameter(lambda x,n: sum(x) / n, stateful=False, loggable=False) + aggregation_function = Parameter(lambda x: np.mean(x,axis=x.ndim-1), stateful=False, loggable=False) search_function = Parameter(lambda x: x, stateful=False, loggable=False) search_termination_function = Parameter(lambda x, y, z: True, stateful=False, loggable=False) search_space = Parameter([SampleIterator([0])], stateful=False, loggable=False) @@ -435,6 +435,7 @@ def __init__( if self.search_space: # Make randomization dimension of search_space last for standardization of treatment self.search_space.append(self.search_space.pop(self.search_space.index(self.randomization_dimension))) + self.randomization_dimension = len(self.search_space) super().__init__( default_variable=default_variable, @@ -603,9 +604,10 @@ def _function(self, # If execution mode is not Python and search_space is static, use parallelized evaluation: if (self.owner.parameters.comp_execution_mode._get(context) != 'Python' and - all(isinstance(sample_iterator.start, int) and isinstance(sample_iterator.stop, int) + all(isinstance(sample_iterator.start, Number) and isinstance(sample_iterator.stop, Number) for sample_iterator in self.search_space)): - last_sample, last_value, all_samples, all_values = self._grid_evaluate(self.owner, context) + all_samples, all_values = self._grid_evaluate(self.owner, context) + last_sample = last_value = None # Otherwise, default sequential sampling else: last_sample, last_value, all_samples, all_values = self._sequential_evaluate(initial_sample, @@ -613,15 +615,14 @@ def _function(self, context) # If aggregation_function is specified, use it to aggregate over randomization dimension - if self.aggregation_function is not None: - randomization_dim = self.parameters.randomization_dimension._get(context) - all_values = np.apply_along_axis(self.aggregation_function, - randomization_dim, - all_values, - self.num_estimates) + if self.aggregation_function: + aggregated_values = self.aggregation_function(all_values) + returned_values = aggregated_values + else: + returned_values = all_values # Return list of unique samples and aggregated values over them - return last_sample, last_value, all_samples, all_values + return last_sample, last_value, all_samples, returned_values def _sequential_evaluate(self, initial_sample, initial_value, context): """Sequentially evaluate every sample in search_space. @@ -650,8 +651,8 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): _progress_bar_count = 0 # Iterate over samples until search_termination_function returns True - samples = [] - values = [] + evaluated_samples = [] + estimated_values = [] while not call_with_pruned_args(self.search_termination_function, current_sample, current_value, iteration, @@ -667,8 +668,8 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): # Get value of sample current_value = call_with_pruned_args(self.objective_function, current_sample, context=context) - samples.append(current_sample) - values.append(current_value) + evaluated_samples.append(current_sample) + estimated_values.append(current_value) self._report_value(current_value) iteration += 1 @@ -688,7 +689,8 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): self.parameters.saved_values._set(all_values, context) # FIX: 11/3/21: ??MODIFY TO RETURN SAME AS _grid_evaluate - return current_sample, current_value, samples, values + # return current_sample, current_value, evaluated_samples, estimated_values + return current_sample, current_value, evaluated_samples, estimated_values def _grid_evaluate(self, ocm, context): """Helper method for parallelizing evaluation of samples from search space. @@ -708,6 +710,7 @@ def _grid_evaluate(self, ocm, context): else: assert False, f"Unknown execution mode for {ocm.name}: {execution_mode}." + # FIX: RETURN SHOULD BE: outcomes, all_samples return outcomes, num_evals def _report_value(self, new_value): From 701da8d423e05e9377d1e468286ded5e1a3ad5e9 Mon Sep 17 00:00:00 2001 From: jdc Date: Wed, 10 Nov 2021 17:29:37 -0500 Subject: [PATCH 062/285] =?UTF-8?q?=E2=80=A2=20optimizationfunctions.py:?= =?UTF-8?q?=20=20=20-=20=5Ffunction:=20refactored=20to=20put=20use=20aggre?= =?UTF-8?q?gation=5Ffunction=20at=20end=20=20=20-=20=5Fgrid=5Fevaluate:=20?= =?UTF-8?q?=20still=20needs=20to=20return=20all=5Fsamples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../functions/nonstateful/optimizationfunctions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index ec9a3dd4387..4c00279fc88 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -606,7 +606,9 @@ def _function(self, if (self.owner.parameters.comp_execution_mode._get(context) != 'Python' and all(isinstance(sample_iterator.start, Number) and isinstance(sample_iterator.stop, Number) for sample_iterator in self.search_space)): - all_samples, all_values = self._grid_evaluate(self.owner, context) + # FIX: NEED TO FIX THIS ONCE _grid_evaluate RETURNS all_samples + all_samples = [] + all_values, num_evals = self._grid_evaluate(self.owner, context) last_sample = last_value = None # Otherwise, default sequential sampling else: @@ -710,7 +712,7 @@ def _grid_evaluate(self, ocm, context): else: assert False, f"Unknown execution mode for {ocm.name}: {execution_mode}." - # FIX: RETURN SHOULD BE: outcomes, all_samples + # FIX: RETURN SHOULD BE: outcomes, all_samples (THEN FIX CALL IN _function) return outcomes, num_evals def _report_value(self, new_value): From 3649bb77db86eadeb17ef3c02674b9043878642e Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 18:14:38 -0500 Subject: [PATCH 063/285] - --- .../components/functions/nonstateful/optimizationfunctions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 4c00279fc88..e4f4a5adcc1 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -379,7 +379,7 @@ class Parameters(Function_Base.Parameters): variable = Parameter(np.array([0, 0, 0]), read_only=True, pnl_internal=True, constructor_argument='default_variable') objective_function = Parameter(lambda x: 0, stateful=False, loggable=False) - aggregation_function = Parameter(lambda x: np.mean(x,axis=x.ndim-1), stateful=False, loggable=False) + aggregation_function = Parameter(lambda x: np.mean(x,axis=len(x)-1), stateful=False, loggable=False) search_function = Parameter(lambda x: x, stateful=False, loggable=False) search_termination_function = Parameter(lambda x, y, z: True, stateful=False, loggable=False) search_space = Parameter([SampleIterator([0])], stateful=False, loggable=False) @@ -618,7 +618,7 @@ def _function(self, # If aggregation_function is specified, use it to aggregate over randomization dimension if self.aggregation_function: - aggregated_values = self.aggregation_function(all_values) + aggregated_values = np.atleast_1d(self.aggregation_function(all_values)) returned_values = aggregated_values else: returned_values = all_values From 89bad354aef5e974e34f8fd5d7b64e81f58da4e7 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 18:17:14 -0500 Subject: [PATCH 064/285] - --- .../components/functions/nonstateful/optimizationfunctions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index e4f4a5adcc1..58465b3be6f 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -603,7 +603,7 @@ def _function(self, # Evaluate all estimates of all samples in search_space # If execution mode is not Python and search_space is static, use parallelized evaluation: - if (self.owner.parameters.comp_execution_mode._get(context) != 'Python' and + if (self.owner and self.owner.parameters.comp_execution_mode._get(context) != 'Python' and all(isinstance(sample_iterator.start, Number) and isinstance(sample_iterator.stop, Number) for sample_iterator in self.search_space)): # FIX: NEED TO FIX THIS ONCE _grid_evaluate RETURNS all_samples @@ -681,7 +681,7 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): break # Change randomization for next sample if specified (relies on randomization being last dimension) - if self.owner.parameters.same_randomization_for_all_allocations is False: + if self.owner and self.owner.parameters.same_randomization_for_all_allocations is False: self.search_space[self.parameters.randomization_dimension._get(context)].start += 1 self.search_space[self.parameters.randomization_dimension._get(context)].stop += 1 From 29fb4d0a5ec04adf1b53f48210e30747ca7ffaf3 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 18:23:30 -0500 Subject: [PATCH 065/285] - --- psyneulink/core/compositions/composition.py | 24 +++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 6d5302b2444..f706d7a77a9 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -7160,17 +7160,19 @@ def add_controller(self, controller:ControlMechanism, context=None): if invalid_aux_components: self._controller_initialization_status = ContextFlags.DEFERRED_INIT - # If the controller doesn't have any feature_input_ports, add ones from Composition's INPUT Nodes - if not controller.feature_input_ports: - input_nodes = self.get_nodes_by_role(NodeRole.INPUT) - for node in input_nodes: - # FIX: 11/3/21 NEED TO DEAL WITH NESTED COMP AS INPUT NODE [MAKE METHOD THAT DOES ALL THIS] - feature_input_ports = [] - for input_port in [input_port for input_port in node.input_ports if not input_port.internal_only]: - feature_input_ports.append(input_port) - # controller._parse_state_feature_specs(controller, feature_input_ports) - controller.add_ports(feature_input_ports, update_variable=False, context=context) - controller.feature_input_ports.append(feature_input_ports) + # # FIX: 11/3/21 COMMENTED OUT SO IT CAN PASS TESTS WHILE REFACTORING OptimizationFunction + # ALSO, GETTING REFACTORED IN refactor/ocm/state_input_ports + # # If the controller doesn't have any feature_input_ports, add ones from Composition's INPUT Nodes + # if not controller.feature_input_ports: + # input_nodes = self.get_nodes_by_role(NodeRole.INPUT) + # for node in input_nodes: + # # FIX: 11/3/21 NEED TO DEAL WITH NESTED COMP AS INPUT NODE [MAKE METHOD THAT DOES ALL THIS] + # feature_input_ports = [] + # for input_port in [input_port for input_port in node.input_ports if not input_port.internal_only]: + # feature_input_ports.append(input_port) + # # controller._parse_state_feature_specs(controller, feature_input_ports) + # controller.add_ports(feature_input_ports, update_variable=False, context=context) + # controller.feature_input_ports.append(feature_input_ports) # FIX: 11/3/21: ISN'T THIS HANDLED IN HANDLING OF aux_components? if self.controller.objective_mechanism and self.controller.objective_mechanism not in invalid_aux_components: From c1b3000d4fa189574212a9485aa09037fdb9edfa Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 19:52:15 -0500 Subject: [PATCH 066/285] =?UTF-8?q?=E2=80=A2=20optimizationcontrolmechanis?= =?UTF-8?q?m.py=20=20=20-=20=5Fgen=5Fllvm=5Fevaluate=5Ffunction:=20num=5Fe?= =?UTF-8?q?stimates=20->=20num=5Ftrials=5Fper=5Festimate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../control/optimizationcontrolmechanism.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 217fdf1c4ae..56a891e9936 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1516,17 +1516,17 @@ def _gen_llvm_evaluate_function(self, *, ctx:pnlvm.LLVMBuilderContext, # - OR TO USE num_trials_per_estimate RATHER THAN num_estimates IF THAT IS WHAT IS INTENDED # # Determine simulation counts - num_estimates_ptr = pnlvm.helpers.get_param_ptr(builder, self, + num_trials_per_estimate_ptr = pnlvm.helpers.get_param_ptr(builder, self, controller_params, - "num_estimates") + "num_trials_per_estimate") - num_estimates = builder.load(num_estimates_ptr, "num_estimates") + num_trials_per_estimate = builder.load(num_trials_per_estimate_ptr, "num_trials_per_estimate") - # if num_estimates is 0, run 1 trial - param_is_zero = builder.icmp_unsigned("==", num_estimates, + # if num_trials_per_estimate is 0, run 1 trial + param_is_zero = builder.icmp_unsigned("==", num_trials_per_estimate, ctx.int32_ty(0)) num_sims = builder.select(param_is_zero, ctx.int32_ty(1), - num_estimates, "corrected_estimates") + num_trials_per_estimate, "corrected_estimates") num_runs = builder.alloca(ctx.int32_ty, name="num_runs") builder.store(num_sims, num_runs) From d1010fd047ce278de2ae337a646d7ca629ebef15 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 20:09:00 -0500 Subject: [PATCH 067/285] - --- psyneulink/core/llvm/builder_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index 29bc0380e8c..f8b8379ca72 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -352,7 +352,7 @@ def _param_struct(p): elif p.name == 'matrix': # Flatten matrix val = np.asfarray(val).flatten() # FIX: NEED TO ADD num_trials_per_estimate HERE - elif p.name == 'num_estimates': # Should always be int + elif p.name == 'num_trials_per_estimate': # Should always be int val = np.int32(0) if val is None else np.int32(val) elif np.ndim(val) == 0 and component._is_param_modulated(p): val = [val] # modulation adds array wrap From 6017b7b2adb68414d3c12796b2bb195b46199fcf Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 10 Nov 2021 20:14:43 -0500 Subject: [PATCH 068/285] - --- psyneulink/core/llvm/builder_context.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index f8b8379ca72..7904fba40ed 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -351,7 +351,8 @@ def _param_struct(p): return ir.LiteralStructType(self.get_param_struct_type(x) for x in val) elif p.name == 'matrix': # Flatten matrix val = np.asfarray(val).flatten() - # FIX: NEED TO ADD num_trials_per_estimate HERE + elif p.name == 'num_estimates': # Should always be int + val = np.int32(0) if val is None else np.int32(val) elif p.name == 'num_trials_per_estimate': # Should always be int val = np.int32(0) if val is None else np.int32(val) elif np.ndim(val) == 0 and component._is_param_modulated(p): From 8edacf64d4d0235dd9e36dba57178decfa3514ef Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Thu, 11 Nov 2021 01:02:05 -0500 Subject: [PATCH 069/285] llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 7 +++---- psyneulink/core/llvm/builder_context.py | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 2b793b1f026..1ba200aa22d 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1363,7 +1363,7 @@ def _get_compilation_params(self): # Invalid types "input_port_variables", "results", "simulation_results", "monitor_for_control", "state_feature_values", "simulation_ids", - "input_labels_dict", "output_labels_dict", + "input_labels_dict", "output_labels_dict", "num_estimates", "modulated_mechanisms", "grid", "control_signal_params", "activation_derivative_fct", "input_specification", # Reference to other components @@ -1448,9 +1448,8 @@ def _get_values(p): # Modulated parameters change shape to array if np.ndim(param) == 0 and self._is_param_modulated(p): return (param,) - elif p.name == 'num_estimates': - return 0 if param is None else param - # FIX: ADD num_trials_per_estimate HERE 11/3/21 + elif p.name == 'num_trials_per_estimate': # Should always be int + return 0 if param is None else int(param) elif p.name == 'matrix': # Flatten matrix return tuple(np.asfarray(param).flatten()) return _convert(param) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index 7904fba40ed..31c94277ca4 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -351,8 +351,6 @@ def _param_struct(p): return ir.LiteralStructType(self.get_param_struct_type(x) for x in val) elif p.name == 'matrix': # Flatten matrix val = np.asfarray(val).flatten() - elif p.name == 'num_estimates': # Should always be int - val = np.int32(0) if val is None else np.int32(val) elif p.name == 'num_trials_per_estimate': # Should always be int val = np.int32(0) if val is None else np.int32(val) elif np.ndim(val) == 0 and component._is_param_modulated(p): From c68e9d8d1c275f4d613a6eaa6702473b1ada8590 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Thu, 11 Nov 2021 07:29:04 -0500 Subject: [PATCH 070/285] =?UTF-8?q?=E2=80=A2=20builder=5Fcontext.py:=20=20?= =?UTF-8?q?=20get=5Fparam=5Fstruct=5Ftype():=20=20restored=20test=20on=20n?= =?UTF-8?q?um=5Festimates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- psyneulink/core/llvm/builder_context.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index 31c94277ca4..7904fba40ed 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -351,6 +351,8 @@ def _param_struct(p): return ir.LiteralStructType(self.get_param_struct_type(x) for x in val) elif p.name == 'matrix': # Flatten matrix val = np.asfarray(val).flatten() + elif p.name == 'num_estimates': # Should always be int + val = np.int32(0) if val is None else np.int32(val) elif p.name == 'num_trials_per_estimate': # Should always be int val = np.int32(0) if val is None else np.int32(val) elif np.ndim(val) == 0 and component._is_param_modulated(p): From 1586db79d9bc155136dabd3758b48583f98fd926 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 2 Dec 2021 16:10:34 -0500 Subject: [PATCH 071/285] refactor Optimization._function int _evaluate --- .../core/components/functions/function.py | 2 +- .../nonstateful/optimizationfunctions.py | 37 ++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index 8e9410baaa8..e39fb415f43 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -628,7 +628,7 @@ def function(self, **kwargs) except ValueError as err: err_msg = f"Problem with '{self}' in '{self.owner.name if self.owner else self.__class__.__name__}': {err}" - raise FunctionError(err_msg) + raise FunctionError(err_msg) from err self.most_recent_context = context self.parameters.value._set(value, context=context) self._reset_runtime_parameters(context) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index 649054a5b47..5a76a5ce0b3 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -379,7 +379,7 @@ class Parameters(Function_Base.Parameters): variable = Parameter(np.array([0, 0, 0]), read_only=True, pnl_internal=True, constructor_argument='default_variable') objective_function = Parameter(lambda x: 0, stateful=False, loggable=False) - aggregation_function = Parameter(lambda x: np.mean(x,axis=len(x)-1), stateful=False, loggable=False) + aggregation_function = Parameter(lambda x: np.mean(x, axis=x.ndim-1), stateful=False, loggable=False) search_function = Parameter(lambda x: x, stateful=False, loggable=False) search_termination_function = Parameter(lambda x, y, z: True, stateful=False, loggable=False) search_space = Parameter([SampleIterator([0])], stateful=False, loggable=False) @@ -586,6 +586,30 @@ def _function(self, for all the samples in the order they were evaluated; otherwise it is empty. """ + raise NotImplementedError("OptimizationFunction._function is not implemented and " + "should be overridden by subclasses.") + + def _evaluate(self, variable=None, context=None, params=None): + """ + Evaluate all the sample in a `search_space ` with the agent_rep. The + evaluation is done either serially (_sequential_evaluate) or in parallel (_grid_evaluate). This method should + be invoked by subclasses in their `_function` method to evaluate the samples before searching for the optimal + value. + + Returns + ------- + + optimal sample, optimal value, saved_samples, saved_values : array, array, list, list + first array contains sample that yields the optimal value of the `optimization process + `, and second array contains the value of `objective_function + ` for that sample. If `save_samples + ` is `True`, first list contains all the values sampled in the order + they were evaluated; otherwise it is empty. If `save_values ` is `True`, + second list contains the values returned by `objective_function ` + for all the samples in the order they were evaluated; otherwise it is empty. + + """ + if self._unspecified_args and self.initialization_status == ContextFlags.INITIALIZED: warnings.warn("The following arg(s) were not specified for {}: {} -- using default(s)". format(self.name, ', '.join(self._unspecified_args))) @@ -626,6 +650,7 @@ def _function(self, # Return list of unique samples and aggregated values over them return last_sample, last_value, all_samples, returned_values + def _sequential_evaluate(self, initial_sample, initial_value, context): """Sequentially evaluate every sample in search_space. Return arrays with all samples evaluated, and array with all values of those samples. @@ -670,6 +695,10 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): # Get value of sample current_value = call_with_pruned_args(self.objective_function, current_sample, context=context) + # Convert the sample and values to numpy arrays even if they are scalars + current_sample = np.atleast_1d(current_sample) + current_value = np.atleast_1d(current_value) + evaluated_samples.append(current_sample) estimated_values.append(current_value) @@ -690,6 +719,10 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): if self.parameters.save_values._get(context): self.parameters.saved_values._set(all_values, context) + # Convert evaluated_samples and estimated_values to numpy arrays, stack along the last dimension + estimated_values = np.stack(estimated_values, axis=estimated_values[0].ndim) + evaluated_samples = np.stack(evaluated_samples, axis=evaluated_samples[0].ndim) + # FIX: 11/3/21: ??MODIFY TO RETURN SAME AS _grid_evaluate # return current_sample, current_value, evaluated_samples, estimated_values return current_sample, current_value, evaluated_samples, estimated_values @@ -1910,7 +1943,7 @@ def _function(self, # Python version else: - last_sample, last_value, all_samples, all_values = super()._function( + last_sample, last_value, all_samples, all_values = self._evaluate( variable=variable, context=context, params=params, From 436f5d9bcd4d2b699b9a3956f4ce1a09aee34676 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Thu, 23 Dec 2021 16:33:49 -0500 Subject: [PATCH 072/285] Feat/comp/inputs (#2259) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • compositioninterfacemechanism.py: - docstring edits * - * - * - * - * - * - * - * - * - * • composition.py: add_projection: _parse_projection_spec -> _instantiate_projection_from_spec * • composition.py: add_projection: _parse_projection_spec -> _instantiate_projection_from_spec * • composition.py - add_linear_processing_pathway: support one->many and many->one configurations in pathway specification (see docstring for method) * - * • composition.py _add_linear_processing_pathway, add_projection, _is_pathway_entry_spec: - support many->one and one->many projections between mech and comps in pathway spec: working - support set of Projections as legal entry in pathway spec: TBI * • composition.py _add_linear_processing_pathway, add_projection, _is_pathway_entry_spec: - support many->one and one->many projections between mech and comps in pathway spec: working - support set of Projections as legal entry in pathway spec: TBI * • show_graph.py _assign_cim_components(): fix bug in which comp1_output_cim->comp2_input_cim was skipped * • compositioninterfacemechanism.py: docstring mod * - * • composition.py: minor formatting * • composition.py add_linear_processing_pathway(): eliminate TARGET Nodes from one->many * - * • composition.py: - add_linear_processing_pathway(): add recursive search for nested INPUT and OUTPUT nodes * • composition.py: - add_linear_processing_pathway(): - exclude Target Nodes from search for INPUT nodes (handled by learning) - add recursive search for nested INPUT and OUTPUT nodes * - * - * • composition.py add_projection: working on adding support for projection sets * - * - * • composition.py - add _get_nested_nodes_with_same_roles_at_all_levels - add_linear_processing_pathway: call _get_nested_nodes_with_same_roles_at_all_levels * • composition.py - add_linear_processing_pathway: add support for inline specification of set of Projections * - * - * • composition.py add_linear_processing_pathway: refactored to support inline specification of sets of Projections * • composition.py add_linear_processing_pathway: docstring edits for Projection set specification * - * - * - * - * - * • test_composition.py: - add test_add_multiple_projections_for_nested_compositions - IN PROGRESS * • test_composition.py: - add test_add_multiple_projections_for_nested_compositions - passes * - * - * • compositioninterfacemechanism.py: add figure and example * • composition.py: - get_inputs_format(): add method - run(): allow inputs arg to take names of nodes * • composition.py: - get_inputs_format(): - implemented num_trials arg - TBI: nested and labels * - * • composition.py - get_input_format(): implement show_nested_input_nodes * • composition.py - get_input_format(): implement use_labels * - * - * - * - * - * - * - * - * - * - * - * • composition.py get_input_format() - working; TBD: tests and documentation in composition docstring * • composition.py get_results_format() - working; TBD: tests and documentation in composition docstring * - * • composition.py: add error messages to _parse_label() and _validate_single_input() * • composition.py: add error messages to _parse_label() and _validate_single_input() * - * • composition.py: docstring mods * • composition.py - reorganize docstring section on execution * - * - * - * - * • test_composition.py / test_input_labels_and_no_orphaning_of_nested_output_nodes: added tests for lables in input * • composition.py: _validate_single_input - get rid of tests (taken care of in _parse_labels) * • test_composition.py/test_input_labels_and_results_by_node_and_no_orphaning_of_nested_output_nodes: - renamed, add test for get_results_by_node * • composition.py - _parse_labels: suppress warning about targets if no learning_components in pway * - * • composition.py: further docstring updates for get_input_format() and get_results_by_node() * - * - * - * - * - * • composition.py - get_results_by_node(): add use_labels option * - * - * - * • composition.py - get_results_by_nodes: allow specification of individual nodes * • composition.py - get_results_by_nodes: allow specification of individual nodes * • composition.py - get_results_by_nodes: allow specification of individual nodes * - * - * • mechanism.py: add input_labels and output_labels as args to constructor * - * - * • project: remove scale from figures * - * - * - * • project - docstring mods • composition.py: found bug with specification of shadows and probes between "parallel" compositions. * • README.rst and composition.py: add links to CGO paper * • README.rst, index.rst and composition.py: - updates to contribtors - add links to CGO paper * • composition.py _update_shadow_projections: report error for failure to find correct_sender * • composition.py - add _check_for_unused_projections(): warns if any projections are specified for Nodes that are not used in the comp * - * - Co-authored-by: Katherine Mantel Co-authored-by: jdcpni --- README.rst | 4 ++ docs/source/index.rst | 5 ++ psyneulink/core/components/ports/inputport.py | 8 +-- psyneulink/core/compositions/composition.py | 59 +++++++++++++++---- psyneulink/core/compositions/showgraph.py | 2 + 5 files changed, 64 insertions(+), 14 deletions(-) diff --git a/README.rst b/README.rst index bbe1d33cbc4..d04ca20b23c 100644 --- a/README.rst +++ b/README.rst @@ -5,6 +5,10 @@ .. image:: https://mybinder.org/badge.svg :target: https://mybinder.org/v2/gh/PrincetonUniversity/PsyNeuLink/master +.. ***************************************************************************************** +.. ****** NOTE: UPDATES TO THIS PAGE SHOULD ALSO BE MADE TO docs/source.index.rst ********* +.. ***************************************************************************************** + Welcome to PsyNeuLink ===================== diff --git a/docs/source/index.rst b/docs/source/index.rst index c02d7a7ddd6..70e1e0ba79b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,6 +3,11 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. ***************************************************************************************** +.. ****** NOTE: UPDATES TO THIS PAGE SHOULD ALSO BE MADE TO README.rst ******************** +.. ***************************************************************************************** + + :tocdepth: 5 .. |logo| image:: _static/PsyNeuLink_logo_no_text.svg diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 03bf08ed93a..44d155d5243 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -510,11 +510,11 @@ --------------- """ +import collections import inspect import numbers import warnings -import collections import numpy as np import typecheck as tc @@ -526,7 +526,7 @@ from psyneulink.core.globals.context import ContextFlags, handle_external_context from psyneulink.core.globals.keywords import \ COMBINE, CONTROL_SIGNAL, EXPONENT, FUNCTION, GATING_SIGNAL, INPUT_PORT, INPUT_PORTS, INPUT_PORT_PARAMS, \ - LEARNING_SIGNAL, MAPPING_PROJECTION, MATRIX, NAME, OPERATION, OUTPUT_PORT, OUTPUT_PORTS, OWNER,\ + LEARNING_SIGNAL, MAPPING_PROJECTION, MATRIX, NAME, OPERATION, OUTPUT_PORT, OUTPUT_PORTS, OWNER, \ PARAMS, PRODUCT, PROJECTIONS, REFERENCE_VALUE, \ SENDER, SHADOW_INPUTS, SHADOW_INPUT_NAME, SIZE, PORT_TYPE, SUM, VALUE, VARIABLE, WEIGHT from psyneulink.core.globals.parameters import Parameter @@ -1417,11 +1417,11 @@ def _parse_shadow_inputs(owner, input_ports): input_ports = convert_to_list(input_ports) input_ports_to_shadow_specs=[] for spec_idx, spec in enumerate(input_ports): - # If {SHADOW_INPUTS:[InputPort or Mechaism,...]} is found: + # If {SHADOW_INPUTS:[InputPort or Mechanism,...]} is found: if isinstance(spec, dict) and SHADOW_INPUTS in spec: input_ports_to_shadow_in_spec=[] # For each item in list of items to shadow specified in that entry: - for item in list(spec[SHADOW_INPUTS]): + for item in convert_to_list(spec[SHADOW_INPUTS]): from psyneulink.core.components.mechanisms.mechanism import Mechanism # If an InputPort was specified, just used that if isinstance(item, InputPort): diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 2e4bed59130..7dd816b5de5 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -3559,7 +3559,7 @@ def __init__( name=name, ) - # core attribute + # core attributes self.graph = Graph() # Graph of the Composition self._graph_processing = None self.nodes = ContentAddressableList(component_type=Component) @@ -3606,6 +3606,7 @@ def __init__( self.needs_update_graph_processing = True # Tracks if the processing graph is current with the full graph self.needs_update_scheduler = True # Tracks if the scheduler needs to be regenerated self.needs_update_controller = True # Tracks if controller needs to update its state_input_ports + self._need_check_for_unused_projections = True self.nodes_to_roles = collections.OrderedDict() self.cycle_vertices = set() @@ -3775,6 +3776,7 @@ def _analyze_graph(self, context=None): # Call again to accomodate any changes from _update_shadow_projections self._determine_node_roles(context=context) self._check_for_projection_assignments(context=context) + self.needs_update_graph = False def _update_processing_graph(self): @@ -3880,6 +3882,8 @@ def add_node(self, node, required_roles=None, context=None): if isinstance(node, ControlMechanism): self._handle_allow_probes_for_control(node) + self._need_check_for_unused_projections = True + def add_nodes(self, nodes, required_roles=None, context=None): """ Add a list of `Nodes ` to the Composition. @@ -5746,6 +5750,7 @@ def _get_sender_at_right_level(shadowed_proj): if correct_sender: original_senders.add(correct_sender) shadow_found = False + # Look for existing shadow_projections from correct_sender to shadowing input_port for shadow_projection in input_port.path_afferents: if shadow_projection.sender == correct_sender: shadow_found = True @@ -5755,6 +5760,14 @@ def _get_sender_at_right_level(shadowed_proj): new_projection = MappingProjection(sender=correct_sender, receiver=input_port) self.add_projection(new_projection, sender=correct_sender, receiver=input_port) + else: + raise CompositionError(f"Unable to find port to shadow ({shadowed_projection.receiver.owner.name}" + f"[{shadowed_projection.receiver.name}]) specified for " + f"{input_port.owner.name}[{input_port.name}] within the same Composition " + f"('{self.name}') as '{input_port.owner.name}' nor any nested within it. " + f"'{shadowed_projection.receiver.owner.name}' may in another Composition " + f"at the same level within '{self.name}' or in an outer Composition, " + f"for which shadowing is not supported.") return original_senders for shadowing_port, shadowed_port in self.shadowing_dict.items(): @@ -5775,8 +5788,9 @@ def _check_for_projection_assignments(self, context=None): """Check that all Projections and Ports with require_projection_in_composition attribute are configured. Validate that all InputPorts with require_projection_in_composition == True have an afferent Projection. - Validate that all OuputStates with require_projection_in_composition == True have an efferent Projection. + Validate that all OutputPorts with require_projection_in_composition == True have an efferent Projection. Validate that all Projections have senders and receivers. + Issue warning if any Projections are to/from nodes not in Composition.projections """ projections = self.projections.copy() @@ -5801,6 +5815,23 @@ def _check_for_projection_assignments(self, context=None): if not projection.receiver: warnings.warn(f'{Projection.__name__} {projection.name} is missing a receiver.') + def _check_for_unused_projections(self, context): + """Warn if there are any Nodes in the Composition, or any nested within it, that are not used. + """ + unused_projections = [] + for node in self.nodes: + if isinstance(node, Composition): + node._check_for_unused_projections(context) + if isinstance(node, Mechanism): + unused_projections.extend([(f"To '{node.name}' from '{proj.sender.owner.name}' ({proj.name})") + for proj in node.afferents if proj not in self.projections]) + unused_projections.extend([(f"From '{node.name}' to '{proj.sender.owner.name}' ({proj.name})") + for proj in node.efferents if proj not in self.projections]) + if unused_projections: + warning = f"\nThe following Projections were specified but are not being used by Nodes in '{self.name}': \n" + warnings.warn(warning + "\n\t".join(unused_projections)) + self._need_check_for_unused_projections = False + def get_feedback_status(self, projection): """Return True if **projection** is designated as a `feedback Projection ` in the Composition, else False. @@ -5846,6 +5877,7 @@ def _check_for_existing_projections(self, err_msg = f"Can't create a {Projection.__name__} from '{sender.name}' to '{receiver.name}': " + err_msg raise CompositionError(err_msg) + # Check for existing Projections from specified sender existing_projections = [proj for proj in sender.efferents if proj.receiver is receiver] existing_projections_in_composition = [proj for proj in existing_projections if proj in self.projections] assert len(existing_projections_in_composition) <= 1, \ @@ -5898,15 +5930,18 @@ def _check_for_nesting_with_absolute_conditions(self, scheduler, termination_con if len(cond.absolute_fixed_points) > 0: fixed_point_conds.add(cond) - warn_str = f'{self} contains a nested Composition, which may cause unexpected behavior in absolute time conditions or failure to terminate execution.' + warn_str = f'{self} contains a nested Composition, which may cause unexpected behavior ' \ + f'in absolute time conditions or failure to terminate execution.' warn = False if len(interval_conds) > 0: warn_str += '\nFor repeating intervals:\n\t' - warn_str += '\n\t'.join([f'{cond.owner}: {cond}\n\t\tintervals: {cond.absolute_intervals}' for cond in interval_conds]) + warn_str += '\n\t'.join([f'{cond.owner}: {cond}\n\t\tintervals: {cond.absolute_intervals}' + for cond in interval_conds]) warn = True if len(fixed_point_conds) > 0: warn_str += '\nIn EXACT_TIME SchedulingMode, strict time points:\n\t' - warn_str += '\n\t'.join([f'{cond.owner}: {cond}\n\t\tstrict time points: {cond.absolute_fixed_points}' for cond in fixed_point_conds]) + warn_str += '\n\t'.join([f'{cond.owner}: {cond}\n\t\tstrict time points: {cond.absolute_fixed_points}' + for cond in fixed_point_conds]) warn = True if warn: @@ -6136,10 +6171,10 @@ def add_linear_processing_pathway(self, pathway, name:str=None, context=None, *a receivers = receivers or convert_to_list(receiver) if len(senders) > 1 and len(receivers) > 1: raise CompositionError(f"Pathway specified with two contiguous Compositions, the first of " - f"which {sender.name} has more than one OUTPUT Node and second of" - f"which {receiver.name} has more than one INPUT Node, making the " - f"configuration of Projections between them ambigous. Please " - f"specify those Projections explicity.") + f"which ({sender.name}) has more than one OUTPUT Node, and second " + f"of which ({receiver.name}) has more than one INPUT Node, making " + f"the configuration of Projections between them ambiguous; please " + f"specify those Projections explicitly.") proj = {self.add_projection(sender=s, receiver=r, allow_duplicates=False) for r in receivers for s in senders} else: @@ -8919,10 +8954,13 @@ def run( # May be used by controller for specifying num_trials_per_simulation self.num_trials = num_trials - # DS 1/7/20: Check to see if any Components are still in deferred init. If so, attempt to initialize them. + # Check to see if any Components are still in deferred init. If so, attempt to initialize them. # If they can not be initialized, raise a warning. self._complete_init_of_partially_initialized_nodes(context=context) + if self._need_check_for_unused_projections: + self._check_for_unused_projections(context=context) + if ContextFlags.SIMULATION_MODE not in context.runmode: self._check_controller_initialization_status() self._check_nodes_initialization_status() @@ -11095,6 +11133,7 @@ def show_graph(self, active_items=None, output_fmt='pdf', context=None): + return self._show_graph(show_node_structure=show_node_structure, show_nested=show_nested, show_nested_args=show_nested_args, diff --git a/psyneulink/core/compositions/showgraph.py b/psyneulink/core/compositions/showgraph.py index 7d3d1b208df..ab439eadbd7 100644 --- a/psyneulink/core/compositions/showgraph.py +++ b/psyneulink/core/compositions/showgraph.py @@ -760,6 +760,8 @@ def show_graph(self, # get all Nodes if output_fmt != 'gv': composition._analyze_graph(context=context) + if composition._need_check_for_unused_projections: + composition._check_for_unused_projections(context) rcvrs = list(processing_graph.keys()) for rcvr in rcvrs: From b383b90c57bc17f5684053cddb217fce45dd72d3 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sat, 25 Dec 2021 23:47:34 -0500 Subject: [PATCH 073/285] Feat/comp/inputs (#2260) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • compositioninterfacemechanism.py: - docstring edits * - * - * - * - * - * - * - * - * - * • composition.py: add_projection: _parse_projection_spec -> _instantiate_projection_from_spec * • composition.py: add_projection: _parse_projection_spec -> _instantiate_projection_from_spec * • composition.py - add_linear_processing_pathway: support one->many and many->one configurations in pathway specification (see docstring for method) * - * • composition.py _add_linear_processing_pathway, add_projection, _is_pathway_entry_spec: - support many->one and one->many projections between mech and comps in pathway spec: working - support set of Projections as legal entry in pathway spec: TBI * • composition.py _add_linear_processing_pathway, add_projection, _is_pathway_entry_spec: - support many->one and one->many projections between mech and comps in pathway spec: working - support set of Projections as legal entry in pathway spec: TBI * • show_graph.py _assign_cim_components(): fix bug in which comp1_output_cim->comp2_input_cim was skipped * • compositioninterfacemechanism.py: docstring mod * - * • composition.py: minor formatting * • composition.py add_linear_processing_pathway(): eliminate TARGET Nodes from one->many * - * • composition.py: - add_linear_processing_pathway(): add recursive search for nested INPUT and OUTPUT nodes * • composition.py: - add_linear_processing_pathway(): - exclude Target Nodes from search for INPUT nodes (handled by learning) - add recursive search for nested INPUT and OUTPUT nodes * - * - * • composition.py add_projection: working on adding support for projection sets * - * - * • composition.py - add _get_nested_nodes_with_same_roles_at_all_levels - add_linear_processing_pathway: call _get_nested_nodes_with_same_roles_at_all_levels * • composition.py - add_linear_processing_pathway: add support for inline specification of set of Projections * - * - * • composition.py add_linear_processing_pathway: refactored to support inline specification of sets of Projections * • composition.py add_linear_processing_pathway: docstring edits for Projection set specification * - * - * - * - * - * • test_composition.py: - add test_add_multiple_projections_for_nested_compositions - IN PROGRESS * • test_composition.py: - add test_add_multiple_projections_for_nested_compositions - passes * - * - * • compositioninterfacemechanism.py: add figure and example * • composition.py: - get_inputs_format(): add method - run(): allow inputs arg to take names of nodes * • composition.py: - get_inputs_format(): - implemented num_trials arg - TBI: nested and labels * - * • composition.py - get_input_format(): implement show_nested_input_nodes * • composition.py - get_input_format(): implement use_labels * - * - * - * - * - * - * - * - * - * - * - * • composition.py get_input_format() - working; TBD: tests and documentation in composition docstring * • composition.py get_results_format() - working; TBD: tests and documentation in composition docstring * - * • composition.py: add error messages to _parse_label() and _validate_single_input() * • composition.py: add error messages to _parse_label() and _validate_single_input() * - * • composition.py: docstring mods * • composition.py - reorganize docstring section on execution * - * - * - * - * • test_composition.py / test_input_labels_and_no_orphaning_of_nested_output_nodes: added tests for lables in input * • composition.py: _validate_single_input - get rid of tests (taken care of in _parse_labels) * • test_composition.py/test_input_labels_and_results_by_node_and_no_orphaning_of_nested_output_nodes: - renamed, add test for get_results_by_node * • composition.py - _parse_labels: suppress warning about targets if no learning_components in pway * - * • composition.py: further docstring updates for get_input_format() and get_results_by_node() * - * - * - * - * - * • composition.py - get_results_by_node(): add use_labels option * - * - * - * • composition.py - get_results_by_nodes: allow specification of individual nodes * • composition.py - get_results_by_nodes: allow specification of individual nodes * • composition.py - get_results_by_nodes: allow specification of individual nodes * - * - * • mechanism.py: add input_labels and output_labels as args to constructor * - * - * • project: remove scale from figures * - * - * - * • project - docstring mods • composition.py: found bug with specification of shadows and probes between "parallel" compositions. * • README.rst and composition.py: add links to CGO paper * • README.rst, index.rst and composition.py: - updates to contribtors - add links to CGO paper * • composition.py _update_shadow_projections: report error for failure to find correct_sender * • composition.py - add _check_for_unused_projections(): warns if any projections are specified for Nodes that are not used in the comp * - * - * - * • test_composition.py: - test_failure_to_find_node_to_shadow(self): - test_unused_projections_warning * • test_composition.py: - test_failure_to_find_node_to_shadow(self): - test_unused_projections_warning * • keywords.py: COMPOSITION: COMPOSITION -> Composition • test_composition.py: - test_failure_to_find_node_to_shadow(self): - test_unused_projections_warning * • keywords.py: COMPOSITION: COMPOSITION -> Composition * - * • show_graph.py: - RANDOMIZATION_CONTROL_PROJECTION: dashed line - docstring mods * - * - * - Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- .../core/components/mechanisms/mechanism.py | 2 +- .../modulatory/control/controlmechanism.py | 2 +- .../modulatory/learning/learningmechanism.py | 5 +- .../compositioninterfacemechanism.py | 2 +- psyneulink/core/compositions/composition.py | 63 +++++----- psyneulink/core/compositions/showgraph.py | 113 +++++++++++------- psyneulink/core/globals/keywords.py | 3 +- tests/composition/test_composition.py | 31 ++++- tests/composition/test_show_graph.py | 100 ++++++++-------- 9 files changed, 189 insertions(+), 132 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index b46b2b5a5dd..268a91ed199 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -810,7 +810,7 @@ 'red' Labels may be used to visualize the input and outputs of Mechanisms in a Composition with the **show_structure** option -of the Composition's `show_graph`show_graph ` method with the keyword **LABELS**. +of the Composition's `show_graph`show_graph ` method with the keyword **LABELS**. >>> C.show_graph(show_mechanism_structure=pnl.LABELS) #doctest: +SKIP diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index d3c8b25f947..a7debad33e4 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -72,7 +72,7 @@ ControlMechanism can be assigned as the `controller ` for a Composition by specifying it in the **controller** argument of the Composition's constructor, or by using the Composition's `add_controller ` method. A Composition's `controller ` and its associated -Components can be displayed using the Composition's `show_graph ` method with its +Components can be displayed using the Composition's `show_graph ` method with its **show_control** argument assigned as `True`. diff --git a/psyneulink/core/components/mechanisms/modulatory/learning/learningmechanism.py b/psyneulink/core/components/mechanisms/modulatory/learning/learningmechanism.py index 663b65a2a6d..c61c02501f5 100644 --- a/psyneulink/core/components/mechanisms/modulatory/learning/learningmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/learning/learningmechanism.py @@ -46,7 +46,7 @@ `, generated by the `primary_learned_projection` and its associated `output_source`. All of the MappingProjection(s) modified by a LearningMechanism must project from one `ProcessingMechanism ` to another in the same `Composition`. The learning components of a Composition can be -displayed using the Composition's `show_graph`show_graph ` method with its +displayed using the Composition's `show_graph`show_graph ` method with its **show_learning** argument assigned `True` or *ALL*. .. _LearningMechanism_Note @@ -370,8 +370,7 @@ Composition, all of the Components required for learning are created automatically. The types of Components that are generated depend on the type of learning specified and the configuration of the `Composition `, as described below. All of the learning Components of a Composition can be displayed using its `show_graph -``show_graph ` method with -the **show_learning** argument assigned `True` or *ALL*. +`), and the value of which is a tuple containing the corresponding (`InputPort`, `OutputPort`) pair used to transmit the information to or from the CompositionInterfaceMechanism. CompositionIntefaceMechanisms can be seen graphically using the `show_cim ` option of the -Composition's `show_graph ` method (see figure below). +Composition's `show_graph ` method (see figure below). .. figure:: _static/CIM_figure.svg diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 7dd816b5de5..f17cb4b7939 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -1135,14 +1135,15 @@ input, and for which only one input is specified (``[1.0]``), which is therefore provided as the input to Mechanism ``c`` on every `TRIAL `. -The input specified for each `Node ` must be compatible with the number of `InputPorts ` -that receive external input for that Node. These are listed in its ``external_input_ports`` attribute (`here -` if it is Mechanism, or `here ` if it is a -Composition). More specifically, the shape of the input value must be compatible with the shape of the Node's -`external_input_values` attribute (`here ` if it is Mechanism, -or `here ` if it is a Composition). While these are always 2d arrays, the number -and size of the items (corresponding to each InputPort) may vary; in some case shorthand notations are allowed, -as illustrated in the `examples ` below. +The key for each entry of the dict can be a direct reference to the `Node `, or the name assigned +to one (i.e., its `name ` attribute). The value must an input that is compatible with the number of +`InputPorts ` that receive external input for that Node. These are listed in its ``external_input_ports`` +(`here ` if it is Mechanism, or `here ` if it +is a Composition). More specifically, the shape of the input value must be compatible with the shape of the Node's +`external_input_values` attribute (`here ` if it is Mechanism, or `here +` if it is a Composition). While these are always 2d arrays, the number and size +of the items (corresponding to each InputPort) may vary; in some case shorthand notations are allowed, as illustrated +in the `examples ` below. .. _Composition_Input_Labels: @@ -1813,7 +1814,7 @@ def input_function(env, result): XXX - ADD DISCUSSION OF show_controller AND show_learning COMMENT -The `show_graph ` method generates a display of the graph structure of `Nodes +The `show_graph ` method generates a display of the graph structure of `Nodes ` and `Projections ` in the Composition based on the Composition's `graph ` (see `Visualization` for additional details). @@ -3212,7 +3213,7 @@ class Composition(Composition_Base, metaclass=ComponentsMeta): ` for additional details). show_graph_attributes : dict : None - specifies features of how the Composition is displayed when its `show_graph ` + specifies features of how the Composition is displayed when its `show_graph ` method is called or **animate** is specified in a call to its `run ` method (see `ShowGraph` for list of attributes and their values). @@ -5761,13 +5762,13 @@ def _get_sender_at_right_level(shadowed_proj): receiver=input_port) self.add_projection(new_projection, sender=correct_sender, receiver=input_port) else: - raise CompositionError(f"Unable to find port to shadow ({shadowed_projection.receiver.owner.name}" - f"[{shadowed_projection.receiver.name}]) specified for " - f"{input_port.owner.name}[{input_port.name}] within the same Composition " - f"('{self.name}') as '{input_port.owner.name}' nor any nested within it. " - f"'{shadowed_projection.receiver.owner.name}' may in another Composition " - f"at the same level within '{self.name}' or in an outer Composition, " - f"for which shadowing is not supported.") + raise CompositionError(f"Unable to find port specified to be shadowed by '{input_port.owner.name}' " + f"({shadowed_projection.receiver.owner.name}" + f"[{shadowed_projection.receiver.name}]) within the same Composition " + f"('{self.name}'), nor in any nested within it. " + f"'{shadowed_projection.receiver.owner.name}' may be in another " + f"Composition at the same level within '{self.name}' or in an outer " + f"Composition, neither of which are supported by shadowing.") return original_senders for shadowing_port, shadowed_port in self.shadowing_dict.items(): @@ -5822,11 +5823,11 @@ def _check_for_unused_projections(self, context): for node in self.nodes: if isinstance(node, Composition): node._check_for_unused_projections(context) - if isinstance(node, Mechanism): - unused_projections.extend([(f"To '{node.name}' from '{proj.sender.owner.name}' ({proj.name})") - for proj in node.afferents if proj not in self.projections]) - unused_projections.extend([(f"From '{node.name}' to '{proj.sender.owner.name}' ({proj.name})") - for proj in node.efferents if proj not in self.projections]) + if isinstance(node, Mechanism): + unused_projections.extend([(f"To '{node.name}' from '{proj.sender.owner.name}' ({proj.name})") + for proj in node.afferents if proj not in self.projections]) + unused_projections.extend([(f"From '{node.name}' to '{proj.receiver.owner.name}' ({proj.name})") + for proj in node.efferents if proj not in self.projections]) if unused_projections: warning = f"\nThe following Projections were specified but are not being used by Nodes in '{self.name}': \n" warnings.warn(warning + "\n\t".join(unused_projections)) @@ -8826,17 +8827,17 @@ def run( details and `ReportDevices` for options. animate : dict or bool : default False - specifies use of the `show_graph`show_graph ` method - to generate a gif movie showing the sequence of Components executed in a run - (see `example `). A dict can be specified containing - options to pass to the `show_graph ` method; each key must be a legal - argument for the `show_graph ` method, and its value a - specification for that argument. The entries listed below can also be included in the dict to specify - parameters of the animation. If the **animate** argument is specified simply as `True`, defaults are - used for all arguments of `show_graph ` and the options below: + specifies use of the `show_graph`show_graph ` method to generate + a gif movie showing the sequence of Components executed in a run (see `example + `). A dict can be specified containing + options to pass to the `show_graph ` method; each key must be a legal + argument for the `show_graph ` method, and its value a specification for that + argument. The entries listed below can also be included in the dict to specify parameters of the + animation. If the **animate** argument is specified simply as `True`, defaults are used for all + arguments of `show_graph ` and the options below: * *UNIT*: *EXECUTION_SET* or *COMPONENT* (default=\\ *EXECUTION_SET*\\ ) -- specifies which Components - to treat as active in each call to `show_graph `. *COMPONENT* generates an + to treat as active in each call to `show_graph() `. *COMPONENT* generates an image for the execution of each Component. *EXECUTION_SET* generates an image for each `execution_set `, showing all of the Components in that set as active. diff --git a/psyneulink/core/compositions/showgraph.py b/psyneulink/core/compositions/showgraph.py index ab439eadbd7..25f11c8051d 100644 --- a/psyneulink/core/compositions/showgraph.py +++ b/psyneulink/core/compositions/showgraph.py @@ -42,16 +42,26 @@ `learning compnents `. These are listed as the arguments for the show_graph ` method below. -*Display attributes* -- state_features (such as the colors and shapes) in which different types of nodes are displayed -can be modified by assigning a dictionary of attribute:values pairs to the **show_graph_configuration** argument of the -Composition's constructor. These are listed as the arguments for the ShowGraph object (used to display the graph) -in the `class reference ` below. +*Display attributes* -- the colors, shapes and arrow styles used in the display can be modified using the +**show_graph_attributes** argument of a Composition's constructor, as described below. -COMMENT: +.. _ShowGraph_Attributes: + +*Display Attributes* +-------------------- + +The default attributes used to display different types of `Components ` and their `roles ` +within a Composition are listed below. These can be customized using the **show_graph_attributes** argument of a +Composition's constructor, in a dict with keys that are any of the parameters listed in `ShowGraph`, and values +that are any supported by `GraphViz `_ for +`shapes `_, +`arrow styles `_ +`colors `_. -The following are the default attribute used to display different types of `Components ` and their `roles -` within a Composition: +Shapes +~~~~~~ +COMMENT: FIX: MAKE FIGURE THAT HAS ALL THE VARIOUS TYPES USING CORRESPONDING NAMES Input Node Singleton Node @@ -60,12 +70,9 @@ ControlMechanism Controller Nested Composition +COMMENT - -Shapes -~~~~~~ - -`Nested Compositions `: square +`Nested Composition `: square `Mechanism`: - default: oval @@ -73,37 +80,38 @@ - `FEEDBACK_SENDER`: octagon - `CONTROLLER`: doubleoctagon -Projection: +`Projection`: - default: arrow - `ControlProjection`: box + - 'RANDOMIZATION_CONTROL_SIGNAL` : dashed line - `MappingProjection` that receives a `LearningProjection` when **show_learning** is True: diamond Colors ~~~~~~ +Component-types +^^^^^^^^^^^^^^^ + +- Control-related components: blue +- Controller-related: purple +- Learning-related components: orange +- Inactive Projection: red +- Active items (when **animate** = True in `run `): **BOLD** + Nodes ^^^^^ - `INPUT`: green - `OUTPUT`: red - `SINGLETON`: brown - -Component-types -^^^^^^^^^^^^^^^ - -Control-related compoments: blue -Controller-related: purple -Learning-related components: orange - -Active items (when **animate**=True in `run `): **BOLD** - -COMMENT + - `CONTROL`: blue + - `CONTROLLER` : purple + - `LEARNING` : orange .. _ShowGraph_Examples_Visualization: *Examples* ---------- - .. _Composition_show_graph_basic_figure: +-----------------------------------------------------------+----------------------------------------------------------+ @@ -201,7 +209,8 @@ from psyneulink.core.components.component import Component from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import ControlMechanism -from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP +from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import \ + AGENT_REP, RANDOMIZATION_CONTROL_SIGNAL from psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism import CompositionInterfaceMechanism from psyneulink.core.components.mechanisms.processing.objectivemechanism import ObjectiveMechanism from psyneulink.core.components.ports.outputport import OutputPort @@ -266,8 +275,10 @@ def __init__(self, error_value): class ShowGraph(): - """ - ShowGraph object with `show_graph ` method for displaying `Composition`. + """ShowGraph object with `show_graph ` method for displaying `Composition`. + + Every Composition is assigned a ShowGraph object, with its `show_graph ` method + assigned to, and callable as Composition.show_graph(). Arguments --------- @@ -303,7 +314,7 @@ class ShowGraph(): composition_shape : default 'rectangle' specifies the shape in which nodes that represent `nested Compositions ` are displayed when **show_nested** is specified as False or a `Composition is nested ` below the - level specified in a call to `show_graph `. + level specified in a call to `show_graph() `. agent_rep_shape : default 'egg' specifies the shape in which the `agent_rep` of an `OptimizationControlMechanism` is displayed. @@ -352,7 +363,7 @@ class ShowGraph(): composition_color : keyword : default 'pink' specifies the color in which nodes that represent `nested Compositions are displayed when **show_nested** is specified as False or a `Composition is nested ` below the - level specified in a call to `show_graph `. + level specified in a call to `show_graph() `. inactive_projection_color : keyword : default 'red' specifies the color in which `Projections ` not active within the `Composition` are displayed, @@ -363,7 +374,7 @@ class ShowGraph(): active_thicker_by : int : default 2 specifies the amount by which to increase the width of the outline of Components specified in the - **active_items** argument of a call to `show_graph `. + **active_items** argument of a call to `show_graph() `. bold_width : int : default 3, specifies the width of the outline for `INPUT` and `OUTPUT` Nodes of the Composition. @@ -492,7 +503,8 @@ def show_graph(self, output_fmt='pdf', \ context=None) - Show graphical display of Components in a Composition's graph. + Show graphical display of Components in a Composition's graph. See `show_graph ` + for additional details. .. note:: This method relies on `graphviz `_, which must be installed and imported @@ -622,9 +634,7 @@ def show_graph(self, - ``source`` -- str with content of G.body """ - # MODIFIED 6/13/20 NEW: from psyneulink.core.compositions.composition import Composition - # MODIFIED 6/13/20 END composition = self.composition @@ -733,6 +743,7 @@ def show_graph(self, # BUILD GRAPH ------------------------------------------------------------------------ import graphviz as gv + self.style = 'solid' G = gv.Digraph( name=composition.name, @@ -1098,7 +1109,8 @@ def _assign_cim_components(self, def _render_projection(_g, proj, sndr_label, rcvr_label, proj_color=self.default_node_color, - arrowhead=self.default_projection_arrow): + arrowhead=self.default_projection_arrow, + style=self.style): if any(item in active_items for item in {proj, proj.sender.owner}): if self.active_color == BOLD: color = proj_color @@ -1115,7 +1127,11 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, else: label = '' - _g.edge(sndr_label, rcvr_label, label=label, color=color, penwidth=proj_width, arrowhead=arrowhead) + _g.edge(sndr_label, rcvr_label, + label=label, color=color, + penwidth=proj_width, + arrowhead=arrowhead, + style=style) for cim in composition.cims: @@ -1233,7 +1249,10 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, sndr_output_node_proj_label = sndr_label # Render Projection - _render_projection(enclosing_g, proj, sndr_output_node_proj_label, rcvr_cim_proj_label, + _render_projection(enclosing_g, + proj, + sndr_output_node_proj_label, + rcvr_cim_proj_label, proj_color) # Projections from input_CIM to INPUT nodes @@ -1405,10 +1424,18 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, else: ctl_proj_color = proj_color or self.control_color - arrowhead = self.default_projection_arrow if isinstance(proj, MappingProjection) else self.control_projection_arrow + if isinstance(proj, MappingProjection): + arrowhead = self.default_projection_arrow + else: + arrowhead = self.control_projection_arrow + + if RANDOMIZATION_CONTROL_SIGNAL in proj.name: + style = 'dashed' + else: + style = self.style _render_projection(g, proj, sndr_param_cim_proj_label, rcvr_modulated_mec_proj_label, - proj_color=ctl_proj_color, arrowhead=arrowhead) + proj_color=ctl_proj_color, arrowhead=arrowhead, style=style) # OUTPUT_CIM ---------------------------------------------------------------------------- @@ -1705,13 +1732,19 @@ def find_rcvr_comp(r, c, l): else: edge_label = '' + if RANDOMIZATION_CONTROL_SIGNAL in ctl_proj.name: + style = 'dashed' + else: + style = self.style + # Construct edge ----------------------------------------------------------------------- g.edge(ctl_proj_sndr_label, ctl_proj_rcvr_label, label=edge_label, color=ctl_proj_color, penwidth=ctl_proj_width, - arrowhead=ctl_proj_arrowhead + arrowhead=ctl_proj_arrowhead, + style = style ) # If controller has objective_mechanism, assign its node and Projections, diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index d27a777ebc0..959cebee2b2 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -385,14 +385,13 @@ def _is_metric(metric): VALUE_ASSIGNMENT = 'VALUE_ASSIGNMENT' FINAL = 'FINAL' - #endregion #region ---------------------------------------------- COMPOSITION ------------------------------------------------- # 11/15/21: FIX - CHANGE TO LOWER CASE FOR USE WITH componentCategory (OR CHANGE THAT?); MAY NEED TO CHANGE TESTS # Composition Categories -COMPOSITION = 'COMPOSITION' +COMPOSITION = 'Composition' AUTODIFF_COMPOSITION = 'AutodiffComposition' COMPOSITION_FUNCTION_APPROXIMATOR = 'CompositionFunctionApproximator' diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 71cdcffdce1..16adf33ff85 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -556,6 +556,19 @@ def test_timing_stress(self, count): print() logger.info('completed {0} addition{2} of a projection to a composition in {1:.8f}s'.format(count, t, 's' if count != 1 else '')) + def test_unused_projections_warning(self): + A = ProcessingMechanism(name='A') + B = ProcessingMechanism(name='B') + C = ProcessingMechanism(name='C', input_ports=[A]) + D = ProcessingMechanism(name='D') + icomp = Composition(name='iCOMP', pathways=[A, B]) + comp1 = Composition(name='COMP_1', pathways=[icomp]) + comp2 = Composition(name='COMP_2', pathways=[C, D]) + with pytest.warns(UserWarning) as warning: + ocomp = Composition(name='OUTER COMPOSITION',pathways=[comp1, comp2]) + ocomp.run() + assert repr(warning[0].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'iCOMP\': \\nFrom \'A\' to \'C\' (MappingProjection from A[OutputPort-0] to C[InputPort-0])"' + assert repr(warning[1].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'COMP_2\': \\nTo \'C\' from \'A\' (MappingProjection from A[OutputPort-0] to C[InputPort-0])"' class TestPathway: @@ -4790,6 +4803,7 @@ def test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_ assert all(isinstance(comp.controller.control_signals[i].efferents[0].receiver.owner, pnl.CompositionInterfaceMechanism) for i in range(4)) + class TestOverloadedCompositions: def test_mechanism_different_inputs(self): a = TransferMechanism(name='a', function=Linear(slope=2)) @@ -5923,7 +5937,6 @@ def test_shadow_internal_projections(self): assert B.value == [[2.0]] assert C.value == [[2.0]] - _test_shadow_nested_nodes_arg =\ [ ('shadow_nodes_one_and_two_levels_deep', 0), @@ -5971,6 +5984,19 @@ def test_shadow_nested_nodes(self, condition): 'that is not an INPUT Node of that Composition is not currently supported.' \ in err.value.error_value + def test_failure_to_find_node_to_shadow(self): + A = ProcessingMechanism(name='A') + B = ProcessingMechanism(name='B') + C = ProcessingMechanism(name='C', input_ports=[A.input_port]) + D = ProcessingMechanism(name='D') + icomp = Composition(name='iCOMP', nodes=[A, B]) + comp1 = Composition(name='COMP_1', pathways=[icomp]) + err_msg = "Unable to find port specified to be shadowed by 'C' (A[InputPort-0]) within the same Composition" + with pytest.raises(CompositionError) as error_value: + comp2 = Composition(name='COMP_2', pathways=[C, D]) + ocomp = Composition(name='OUTER COMPOSITION',pathways=[comp1, comp2]) + assert err_msg in str(error_value) + def test_monitor_input_ports(self): comp = Composition(name='comp') @@ -6116,6 +6142,7 @@ def test_initialize_cycles_warning(self): with pytest.warns(UserWarning, match=warning_text): comp.run(initialize_cycle_values={a: 1}) + class TestResetValues: def test_reset_one_mechanism_through_run(self): @@ -6419,8 +6446,6 @@ def test_input_labels_and_results_by_node_and_no_orphaning_of_nested_output_node ocomp.get_results_by_nodes(nodes=['N']) assert no_such_node_error_msg in str(error_text.value) - - def test_unnested_PROBE(self): A = ProcessingMechanism(name='A') B = ProcessingMechanism(name='B') diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index 40a235a97cd..f8f6b1e1460 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -241,11 +241,11 @@ def test_converging_pathways(self): ), ( {'show_controller': True}, - '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\t"ocomp INPUT_CIM" -> "my ocm" [label="" arrowhead=normal 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}' + '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 style=solid]\n\t"my ocm" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"my ocm" -> ib [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> "my ocm" [label="" arrowhead=normal 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}' ), ( {'show_controller': True, 'show_node_structure': True}, - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> "my ocm":"InputPort-Shadowed input of of ia[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + '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 style=solid]\n\t"my ocm":"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"my ocm":"OutputPort-ib[slope] ControlSignal" -> ib:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> "my ocm":"InputPort-Shadowed input of of ia[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ) ] @@ -295,7 +295,7 @@ def test_no_nested_and_controler_name_with_space_in_it( ), ( {'show_nested': 2, 'show_cim': True, 'show_node_structure': True}, - '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_CIM":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ma[slope] ControlSignal" -> "mcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ma_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"mcomp OUTPUT_CIM":"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_CIM" [label=<
INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_RESULT" -> mb:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"mcomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"mcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ma_slope" -> ma:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"mcomp OUTPUT_CIM" [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_CIM":"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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"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}', + '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_CIM":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ma[slope] ControlSignal" -> "mcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ma_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"mcomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_mb_RESULT" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [label=<
INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_RESULT" -> mb:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"mcomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"mcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ma_slope" -> ma:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"mcomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_mb_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ib_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', ), ] @@ -338,11 +338,11 @@ def test_multiple_nesting_levels_with_control_mech_projection_one_level_deep( ), ( {'show_nested': False, 'show_cim': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n}', + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n}', ), ( {'show_nested': NESTED, 'show_cim': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION OUTPUT_CIM" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> Target [label="" arrowhead=normal color=black penwidth=1]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"INNER OUTPUT" -> "NESTED COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}', + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> Target [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"INNER OUTPUT" -> "NESTED COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}', ), ( {'show_nested': False, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, @@ -354,11 +354,11 @@ def test_multiple_nesting_levels_with_control_mech_projection_one_level_deep( ), ( {'show_nested': False, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n}', + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n}', ), ( {'show_nested': NESTED, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}', + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}', ), ] @@ -382,35 +382,35 @@ def test_nested_learning(self, show_graph_kwargs, expected_output): _nested_learning_test_with_user_specified_target_in_outer_composition_data = [ ( {'show_nested': False, 'show_cim': False, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' ), ( {'show_nested': NESTED, 'show_cim': False, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> Target [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> Target [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> TARGET [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" -> TARGET [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' ), ( {'show_nested': NESTED, 'show_cim': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION OUTPUT_CIM" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> TARGET [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> Target [label="" arrowhead=normal color=black penwidth=1]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"INNER OUTPUT" -> "NESTED COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" -> TARGET [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> Target [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"INNER OUTPUT" -> "NESTED COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ] @@ -502,18 +502,18 @@ def test_nested_learning_test_with_user_specified_target_in_outer_composition( # each item corresponds to the same item in _nested_show_graph_kwargs above _of_show_nested_show_cim_and_show_node_structure_expected_outputs = [ - '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', - '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', - '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 -> ia [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> ia [label="" arrowhead=box color=blue penwidth=1]\n\tia -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', - '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', - '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [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}', - '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_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [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}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"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}', - '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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT_CIM":"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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp PARAMETER_CIM":"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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"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}' + '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', + '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', + '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 -> ia [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> ia [label="" arrowhead=box color=blue penwidth=1]\n\tia -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', + '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', + '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', + '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}' ] @pytest.mark.parametrize( @@ -556,18 +556,18 @@ def test_of_show_nested_show_cim_and_show_node_structure( # each item corresponds to the same item in _nested_show_graph_kwargs above _of_show_3_level_nested_show_cim_and_show_node_structure_outputs = [ - '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', - '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma -> 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 [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}', - '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 -> ma [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> ia [label="" arrowhead=box color=blue penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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 -> ia [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 [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}', - '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', - '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> midcomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> midcomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT_CIM" -> ma [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\ticomp -> "midcomp OUTPUT_CIM" [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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT_CIM" [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}', - '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_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1]\n\t"midcomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT_CIM" -> ma [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"icomp OUTPUT_CIM" -> "midcomp OUTPUT_CIM" [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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT_CIM" [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}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"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}', - '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_CIM":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"midcomp OUTPUT_CIM":"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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "midcomp PARAMETER_CIM":"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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> "midcomp OUTPUT_CIM":"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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"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}', + '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', + '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma -> 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 [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}', + '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 -> ma [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> ia [label="" arrowhead=box color=blue penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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 -> ia [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 [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}', + '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', + '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT_CIM" -> ma [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\ticomp -> "midcomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"midcomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT_CIM" -> ma [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"icomp OUTPUT_CIM" -> "midcomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> icomp [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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_CIM":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"midcomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> "midcomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', ] @pytest.mark.parametrize( @@ -615,17 +615,17 @@ def test_of_show_3_level_nested_show_cim_and_show_node_structure( # each item corresponds to the same item in _nested_show_graph_kwargs above _of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer_comp_added_last_outputs = [ - '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', - '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', - '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 -> ia [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> ia [label="" arrowhead=box color=blue penwidth=1]\n\tia -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', - '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', - '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [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}', - '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_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [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}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', - '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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"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\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"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}', + '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', + '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', + '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 -> ia [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> ia [label="" arrowhead=box color=blue penwidth=1]\n\tia -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', + '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', + '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', + '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', # FIX: NEEDS TO BE CORRECTED ONCE BUG IS FIXED (SEE MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c) # '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}' ] From cb18e77e805ed6e0ef1a4f686881d2d60eece204 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Mon, 27 Dec 2021 17:31:59 -0500 Subject: [PATCH 074/285] Fix/control/bugs (#2261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • component.py docstring mod to **size** * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • composition.py: - _add_node_aux_components(): fix bug in which ControlProjections were getting replaced by MappingProjections in composition.projections * • show_graph.py - fix bug in check for name of RANDOMIZATION_CONTROL_SIGNAL * • optimizationcontrolmechanism.py: - _create_randomization_control_signal(): warn if num_estimates is >1 and there are no random_variables • composition.py: - add_controller: add test for num_estimates before calling _create_randomization_control_signal() • test_control - test_mode_based_num_estimates: add test for random variables (using DDM) * - * - * - Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- .../control/optimizationcontrolmechanism.py | 184 +++++++++--------- psyneulink/core/compositions/composition.py | 37 ++-- psyneulink/core/compositions/showgraph.py | 4 +- psyneulink/core/llvm/builder_context.py | 3 +- tests/composition/test_composition.py | 54 ++++- tests/composition/test_control.py | 57 ++++-- tests/composition/test_show_graph.py | 2 + 7 files changed, 209 insertions(+), 132 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 6c15e5b26bf..5b658b6239b 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -607,7 +607,7 @@ - It must accept as its first argument and return as its result an array with the same shape as the OptimizationControlMechanism's `control_allocation `. .. - - It must execute the OptimizationControlMechanism's `evaluate_agent_rep + - It must be able to execute the OptimizationControlMechanism's `evaluate_agent_rep ` `num_estimates ` times, and aggregate the results in computing the `net_outcome ` for a given `control_allocation ` (see @@ -661,14 +661,19 @@ *Randomization ControlSignal* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If `num_estimates ` is specified (that is, it is not None), -a `ControlSignal` is automatically added to the OptimizationControlMechanism's `control_signals -`, named *RANDOMIZATION_CONTROL_SIGNAL*, that randomizes -the values of random variables in the `agent_rep ` over estimates of its -`net_outcome `. The `initial_seed ` and -`same_seed_for_all_allocations ` Parameters can also be -used to further refine randomization (see `OptimizationControlMechanism_Estimation_Randomization` for additional -details). +If `num_estimates ` is specified (that is, it is not None), and +`agent_rep ` has any `Components ` with random variables +(that is, that call a randomization function) specified in the OptimizationControlMechanism's `random_variables +` attribute, then a `ControlSignal` is automatically added to the +OptimizationControlMechanism's `control_signals `, named +*RANDOMIZATION_CONTROL_SIGNAL*, that randomizes the values of the `random variables +` over estimates of its `net_outcome ` +for each `control_allocation ` If `num_estimates +` is specified but `agent_rep ` +has not random variables, then a warning is issued and no *RANDOMIZATION_CONTROL_SIGNAL* is constructed. The +`initial_seed ` and `same_seed_for_all_allocations +` Parameters can also be used to further refine +randomization (see `OptimizationControlMechanism_Estimation_Randomization` for additional details). .. technical_note:: @@ -765,7 +770,7 @@ ` times (i.e., by that number of calls to the OptimizationControlMechanism's `evaluate_agent_rep ` method). The values of Components listed in the OptimizationControlMechanism's `random_variables -` attribute are randomized over thoese estimates. By default, +` attribute are randomized over those estimates. By default, this includes all Components in the `agent_rep ` with random variables (listed in its `random_variables ` attribute). However, if particular Components are specified in the **random_variables** argument of the OptimizationControlMechanism's constructor, then randomization is @@ -857,7 +862,7 @@ __all__ = [ 'OptimizationControlMechanism', 'OptimizationControlMechanismError', - 'AGENT_REP', 'STATE_FEATURES', 'STATE_FEATURE_FUNCTIONS', 'RANDOMIZATION_CONTROL_SIGNAL' + 'AGENT_REP', 'STATE_FEATURES', 'STATE_FEATURE_FUNCTIONS', 'RANDOMIZATION_CONTROL_SIGNAL', 'NUM_ESTIMATES' ] AGENT_REP = 'agent_rep' @@ -865,6 +870,7 @@ STATE_FEATURE_FUNCTIONS = 'state_feature_functions' RANDOMIZATION_CONTROL_SIGNAL = 'RANDOMIZATION_CONTROL_SIGNAL' RANDOM_VARIABLES = 'random_variables' +NUM_ESTIMATES = 'num_estimates' def _parse_state_feature_values_from_variable(index, variable): """Return values of state_input_ports""" @@ -926,7 +932,8 @@ class OptimizationControlMechanism(ControlMechanism): state_feature_functions : Function or function : default None specifies the `function ` assigned the `InputPort` in `state_input_ports ` assigned to each **state_feature** - (see `state_feature_functions ` for additional details). + (see `state_feature_functions ` + for additional details). agent_rep : None or Composition : default None or Composition to which OptimizationControlMechanism is assigned specifies the `Composition` used by `evaluate_agent_rep ` @@ -940,18 +947,22 @@ class OptimizationControlMechanism(ControlMechanism): the `agent_rep `. num_estimates : int : 1 - specifies the number independent runs of `agent_rep ` used - to estimate its `net_outcome ` for each `control_allocation - ` sampled (see `num_estimates + specifies the number independent runs of `agent_rep ` randomized + over **random_variables** and used to estimate its `net_outcome ` for each + `control_allocation ` sampled (see `num_estimates ` for additional information). random_variables : Parameter or list[Parameter] : default ALL - specifies the Components with random variables to be randomized over different estimates - of each `control_allocation `; these must be in the `agent_rep - ` and have a `seed` `Parameter`. By default, all such Components in - the `agent_rep ` (listed in its `random_variables - ` attribute) are included (see `random_variables - ` for additional information). + specifies the Components of `agent_rep ` with random variables to be + randomized over different estimates of each `control_allocation `; these + must be in the `agent_rep ` and have a `seed` `Parameter`. By default, + all such Components (listed in its `random_variables ` attribute) are included + (see `random_variables ` for additional information). + + .. note:: + if **num_estimates** is specified but `agent_rep ` has no + `random variables `, a warning is generated and `num_estimates + ` is set to None. initial_seed : int : default None specifies the seed used to initialize the random number generator at construction. @@ -1045,8 +1056,9 @@ class OptimizationControlMechanism(ControlMechanism): `OptimizationControlMechanism_Estimation_Randomization` for additional details. random_variables : Parameter or List[Parameter] - list of the Components with variables that are randomized over estimates for a given `control_allocation - `; by default, all Components in the `agent_rep + list of the `Parameters ` in `agent_rep ` with random + variables (that is, ones that call a randomization function) that are randomized over estimates for a given + `control_allocation `; by default, all Components in the `agent_rep ` with random variables are included (listed in its `random_variables ` attribute); see `OptimizationControlMechanism_Estimation_Randomization` for additional details. @@ -1730,17 +1742,66 @@ def _instantiate_control_signals(self, context): # MODIFIED 11/20/21 END self.output_ports[i] = control_signal - self._create_randomization_control_signal( - context, - set_control_signal_index=False - ) - - self.defaults.value = np.tile( - control_signal.parameters.variable.default_value, - (len(self.output_ports), 1) - ) + self._create_randomization_control_signal(context, set_control_signal_index=False) + self.defaults.value = np.tile(control_signal.parameters.variable.default_value, (len(self.output_ports), 1)) self.parameters.control_allocation._set(copy.deepcopy(self.defaults.value), context) + def _create_randomization_control_signal(self, context, set_control_signal_index=True): + if self.num_estimates: + # must be SampleSpec in allocation_samples arg + randomization_seed_mod_values = SampleSpec(start=1, stop=self.num_estimates, step=1) + + # FIX: 11/3/21 noise PARAM OF TransferMechanism IS MARKED AS SEED WHEN ASSIGNED A DISTRIBUTION FUNCTION, + # BUT IT HAS NO PARAMETER PORT BECAUSE THAT PRESUMABLY IS FOR THE INTEGRATOR FUNCTION, + # BUT THAT IS NOT FOUND BY model.all_dependent_parameters + # Get Components with variables to be randomized across estimates + # and construct ControlSignal to modify their seeds over estimates + if self.random_variables is ALL: + self.random_variables = self.agent_rep.random_variables + + if not self.random_variables: + warnings.warn(f"'{self.name}' has '{NUM_ESTIMATES} = {self.num_estimates}' specified, " + f"but its '{AGENT_REP}' ('{self.agent_rep.name}') has no random variables: " + f"'{RANDOMIZATION_CONTROL_SIGNAL}' will not be created, and num_estimates set to None.") + self.num_estimates = None + return + + randomization_control_signal = ControlSignal(name=RANDOMIZATION_CONTROL_SIGNAL, + modulates=[param.parameters.seed.port + for param in self.random_variables], + allocation_samples=randomization_seed_mod_values) + randomization_control_signal_index = len(self.output_ports) + randomization_control_signal._variable_spec = (OWNER_VALUE, randomization_control_signal_index) + + randomization_control_signal = self._instantiate_control_signal(randomization_control_signal, context) + + self.output_ports.append(randomization_control_signal) + + # Otherwise, assert that num_estimates and number of seeds generated by randomization_control_signal are equal + num_seeds = self.control_signals[RANDOMIZATION_CONTROL_SIGNAL].parameters.allocation_samples._get(context).num + assert self.num_estimates == num_seeds, \ + f"PROGRAM ERROR: The value of the {NUM_ESTIMATES} Parameter of {self.name}" \ + f"({self.num_estimates}) is not equal to the number of estimates that will be generated by " \ + f"its {RANDOMIZATION_CONTROL_SIGNAL} ControlSignal ({num_seeds})." + + function_search_space = self.function.parameters.search_space._get(context) + if randomization_control_signal_index >= len(function_search_space): + # TODO: check here if search_space has an item for each + # control_signal? or is allowing it through for future + # checks the right way? + + # search_space must be a SampleIterator + function_search_space.append(SampleIterator(randomization_seed_mod_values)) + + # workaround for fact that self.function.reset call in + # _instantiate_attributes_after_function expects to use + # old/unset values when running _update_default_variable, + # which calls self.agent_rep.evaluate and is brittle. + if set_control_signal_index: + self.function.parameters.randomization_dimension._set( + randomization_control_signal_index, context + ) + def _instantiate_function(self, function, function_params=None, context=None): # this indicates a significant peculiarity of OCM, in that its function # corresponds to its value (control_allocation) rather than anything to @@ -1948,65 +2009,6 @@ def evaluate_agent_rep(self, control_allocation, context=None, return_results=Fa context=context ) - def _create_randomization_control_signal( - self, - context, - set_control_signal_index=True - ): - if self.num_estimates: - # must be SampleSpec in allocation_samples arg - randomization_seed_mod_values = SampleSpec(start=1, stop=self.num_estimates, step=1) - - # FIX: 11/3/21 noise PARAM OF TransferMechanism IS MARKED AS SEED WHEN ASSIGNED A DISTRIBUTION FUNCTION, - # BUT IT HAS NO PARAMETER PORT BECAUSE THAT PRESUMABLY IS FOR THE INTEGRATOR FUNCTION, - # BUT THAT IS NOT FOUND BY model.all_dependent_parameters - # Get Components with variables to be randomized across estimates - # and construct ControlSignal to modify their seeds over estimates - if self.random_variables is ALL: - self.random_variables = self.agent_rep.random_variables - - randomization_control_signal = ControlSignal( - name=RANDOMIZATION_CONTROL_SIGNAL, - modulates=[ - param.parameters.seed.port - for param in self.random_variables - ], - allocation_samples=randomization_seed_mod_values - ) - randomization_control_signal_index = len(self.output_ports) - randomization_control_signal._variable_spec = ( - OWNER_VALUE, randomization_control_signal_index - ) - randomization_control_signal = self._instantiate_control_signal( - randomization_control_signal, context - ) - self.output_ports.append(randomization_control_signal) - - # Otherwise, assert that num_estimates and number of seeds generated by randomization_control_signal are equal - num_seeds = self.control_signals[RANDOMIZATION_CONTROL_SIGNAL].parameters.allocation_samples._get(context).num - assert self.num_estimates == num_seeds, \ - f"PROGRAM ERROR: The value of the 'num_estimates' Parameter of {self.name}" \ - f"({self.num_estimates}) is not equal to the number of estimates that will be generated by " \ - f"its {RANDOMIZATION_CONTROL_SIGNAL} ControlSignal ({num_seeds})." - - function_search_space = self.function.parameters.search_space._get(context) - if randomization_control_signal_index >= len(function_search_space): - # TODO: check here if search_space has an item for each - # control_signal? or is allowing it through for future - # checks the right way? - - # search_space must be a SampleIterator - function_search_space.append(SampleIterator(randomization_seed_mod_values)) - - # workaround for fact that self.function.reset call in - # _instantiate_attributes_after_function expects to use - # old/unset values when running _update_default_variable, - # which calls self.agent_rep.evaluate and is brittle. - if set_control_signal_index: - self.function.parameters.randomization_dimension._set( - randomization_control_signal_index, context - ) - def _get_evaluate_input_struct_type(self, ctx): # We construct input from optimization function input return ctx.get_input_struct_type(self.function) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index f17cb4b7939..79ec1e97f7d 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2572,7 +2572,7 @@ def input_function(env, result): from psyneulink.core.components.mechanisms.mechanism import Mechanism_Base, MechanismError, MechanismList from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import ControlMechanism from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP, \ - RANDOMIZATION_CONTROL_SIGNAL + RANDOMIZATION_CONTROL_SIGNAL, NUM_ESTIMATES from psyneulink.core.components.mechanisms.modulatory.learning.learningmechanism import \ LearningMechanism, ACTIVATION_INPUT_INDEX, ACTIVATION_OUTPUT_INDEX, ERROR_SIGNAL, ERROR_SIGNAL_INDEX from psyneulink.core.components.mechanisms.modulatory.modulatorymechanism import ModulatoryMechanism_Base @@ -4314,8 +4314,8 @@ def _add_node_aux_components(self, node, context=None): sender_node = proj_spec[0].sender.owner.owner_mech if isinstance(receiver_node, AutoAssociativeProjection): receiver_node = proj_spec[0].receiver.owner.owner_mech - if sender_node in self.nodes and \ - receiver_node in self.nodes: + if sender_node in self._all_nodes and \ + receiver_node in self._all_nodes: self.add_projection(projection=proj_spec[0], feedback=proj_spec[1]) else: @@ -5824,13 +5824,13 @@ def _check_for_unused_projections(self, context): if isinstance(node, Composition): node._check_for_unused_projections(context) if isinstance(node, Mechanism): - unused_projections.extend([(f"To '{node.name}' from '{proj.sender.owner.name}' ({proj.name})") + unused_projections.extend([(f"{proj.name} (to '{node.name}' from '{proj.sender.owner.name}')") for proj in node.afferents if proj not in self.projections]) - unused_projections.extend([(f"From '{node.name}' to '{proj.receiver.owner.name}' ({proj.name})") + unused_projections.extend([(f"{proj.name} (from '{node.name}' to '{proj.receiver.owner.name}')") for proj in node.efferents if proj not in self.projections]) if unused_projections: - warning = f"\nThe following Projections were specified but are not being used by Nodes in '{self.name}': \n" - warnings.warn(warning + "\n\t".join(unused_projections)) + warning = f"\nThe following Projections were specified but are not being used by Nodes in '{self.name}':" + warnings.warn(warning + "\n\t" + "\n\t".join(unused_projections)) self._need_check_for_unused_projections = False def get_feedback_status(self, projection): @@ -7668,17 +7668,18 @@ def add_controller(self, controller:ControlMechanism, context=None): for node in self.nodes: self._instantiate_deferred_init_control(node, context) - if RANDOMIZATION_CONTROL_SIGNAL not in self.controller.output_ports.names: - try: - self.controller._create_randomization_control_signal(context) - except AttributeError: - # ControlMechanism does not use RANDOMIZATION_CONTROL_SIGNAL - pass - else: - self.controller.function.parameters.randomization_dimension._set( - self.controller.output_ports.names.index(RANDOMIZATION_CONTROL_SIGNAL), - context - ) + if hasattr(self.controller, NUM_ESTIMATES) and self.controller.num_estimates: + if RANDOMIZATION_CONTROL_SIGNAL not in self.controller.output_ports.names: + try: + self.controller._create_randomization_control_signal(context) + except AttributeError: + # ControlMechanism does not use RANDOMIZATION_CONTROL_SIGNAL + pass + else: + self.controller.function.parameters.randomization_dimension._set( + self.controller.output_ports.names.index(RANDOMIZATION_CONTROL_SIGNAL), + context + ) # ACTIVATE FOR COMPOSITION ----------------------------------------------------- diff --git a/psyneulink/core/compositions/showgraph.py b/psyneulink/core/compositions/showgraph.py index 25f11c8051d..f6620abdf11 100644 --- a/psyneulink/core/compositions/showgraph.py +++ b/psyneulink/core/compositions/showgraph.py @@ -1429,7 +1429,7 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, else: arrowhead = self.control_projection_arrow - if RANDOMIZATION_CONTROL_SIGNAL in proj.name: + if RANDOMIZATION_CONTROL_SIGNAL in proj.sender.name: style = 'dashed' else: style = self.style @@ -1732,7 +1732,7 @@ def find_rcvr_comp(r, c, l): else: edge_label = '' - if RANDOMIZATION_CONTROL_SIGNAL in ctl_proj.name: + if RANDOMIZATION_CONTROL_SIGNAL in ctl_proj.sender.name: style = 'dashed' else: style = self.style diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index 42a95aa9fc1..48516a81516 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -340,6 +340,7 @@ def get_output_struct_type(self, component): @_comp_cached def get_param_struct_type(self, component): + from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import NUM_ESTIMATES self._stats["param_structs_generated"] += 1 if hasattr(component, '_get_param_struct_type'): return component._get_param_struct_type(self) @@ -353,7 +354,7 @@ def _param_struct(p): return ir.LiteralStructType(self.get_param_struct_type(x) for x in val) elif p.name == 'matrix': # Flatten matrix val = np.asfarray(val).flatten() - elif p.name == 'num_estimates': # Should always be int + elif p.name == NUM_ESTIMATES: # Should always be int val = np.int32(0) if val is None else np.int32(val) elif p.name == 'num_trials_per_estimate': # Should always be int val = np.int32(0) if val is None else np.int32(val) diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 16adf33ff85..a4da72f7419 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -25,6 +25,7 @@ from psyneulink.core.components.ports.inputport import InputPort from psyneulink.core.components.ports.modulatorysignals.controlsignal import ControlSignal, CostFunctions from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection +from psyneulink.core.components.projections.modulatory.controlprojection import ControlProjection from psyneulink.core.compositions.composition import Composition, CompositionError, NodeRole from psyneulink.core.compositions.pathway import Pathway, PathwayRole from psyneulink.core.globals.context import Context @@ -567,8 +568,9 @@ def test_unused_projections_warning(self): with pytest.warns(UserWarning) as warning: ocomp = Composition(name='OUTER COMPOSITION',pathways=[comp1, comp2]) ocomp.run() - assert repr(warning[0].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'iCOMP\': \\nFrom \'A\' to \'C\' (MappingProjection from A[OutputPort-0] to C[InputPort-0])"' - assert repr(warning[1].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'COMP_2\': \\nTo \'C\' from \'A\' (MappingProjection from A[OutputPort-0] to C[InputPort-0])"' + assert repr(warning[0].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'iCOMP\':\\n\\tMappingProjection from A[OutputPort-0] to C[InputPort-0] (from \'A\' to \'C\')"' + assert repr(warning[1].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'COMP_2\':\\n\\tMappingProjection from A[OutputPort-0] to C[InputPort-0] (to \'C\' from \'A\')"' + class TestPathway: @@ -1260,6 +1262,54 @@ def test_composition_learning_pathway_dict_with_no_learning_fct_in_tuple_error(s assert ("The 2nd item" in str(error_text.value) and "must be a LearningFunction" in str(error_text.value)) +class TestProperties: + + def test_properties(self): + + Input = pnl.TransferMechanism(name='Input') + Reward = pnl.TransferMechanism(output_ports=[pnl.RESULT, pnl.MEAN, pnl.VARIANCE], name='reward') + Decision = pnl.DDM(function=pnl.DriftDiffusionAnalytical, + output_ports=[pnl.DECISION_VARIABLE, + pnl.RESPONSE_TIME, + pnl.PROBABILITY_UPPER_THRESHOLD], + name='Decision') + task_execution_pathway = [Input, pnl.IDENTITY_MATRIX, (Decision, NodeRole.OUTPUT)] + comp = pnl.Composition(name="evc", retain_old_simulation_data=True, pathways=[task_execution_pathway, + Reward]) + comp.add_controller( + controller=pnl.OptimizationControlMechanism( + agent_rep=comp, + num_estimates=2, + state_features=[Input.input_port, Reward.input_port], + state_feature_functions=pnl.AdaptiveIntegrator(rate=0.1), + monitor_for_control=[Reward, + Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], + Decision.output_ports[pnl.RESPONSE_TIME]], + function=pnl.GridSearch(), + control_signals=[{PROJECTIONS: ("drift_rate", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, + {PROJECTIONS: ("threshold", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, + {PROJECTIONS: ("slope", Reward), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}])) + + assert len(comp.nodes) == len(comp.mechanisms) == 3 + assert len(list(comp._all_nodes)) == 7 + assert len(comp.input_ports) == len(comp.get_nodes_by_role(NodeRole.INPUT)) == 2 + assert len(comp.external_input_ports) == len(comp.input_ports) + assert len(comp.output_ports) / 3 == len(comp.get_nodes_by_role(NodeRole.OUTPUT)) == 2 + assert len(comp.shadowing_dict) == 2 + assert len(comp.stateful_nodes) == 0 + assert len(comp.stateful_parameters) == 10 + assert len(comp.random_variables) == 2 + assert len(comp._dependent_components) == 25 + assert len(comp.afferents) == len(comp.efferents) == 0 + assert isinstance(comp.controller, OptimizationControlMechanism) + assert len(comp.projections) == 18 + assert len([proj for proj in comp.projections if isinstance(proj, MappingProjection)]) == 14 + assert len([proj for proj in comp.projections if isinstance(proj, ControlProjection)]) == 4 + + class TestAnalyzeGraph: def test_empty_call(self): diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index da45a9d2d8f..693b04b8c4c 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -2400,12 +2400,17 @@ def computeAccuracy(trialInformation): inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain} stabilityFlexibility.run(inputs) - @pytest.mark.parametrize('num_estimates',[None, 1] ) - def test_model_based_num_estimates(self, num_estimates): + @pytest.mark.parametrize('num_estimates',[None, 1, 2] ) + @pytest.mark.parametrize('rand_var',[False, True] ) + def test_model_based_num_estimates(self, num_estimates, rand_var): A = pnl.ProcessingMechanism(name='A') - B = pnl.ProcessingMechanism(name='B', - function=pnl.SimpleIntegrator(rate=1)) + if rand_var: + B = pnl.DDM(name='B', + function=pnl.DriftDiffusionAnalytical) + else: + B = pnl.ProcessingMechanism(name='B', + function=pnl.SimpleIntegrator(rate=1)) comp = pnl.Composition(name='comp') comp.add_linear_processing_pathway([A, B]) @@ -2417,28 +2422,44 @@ def test_model_based_num_estimates(self, num_estimates): intensity_cost_function=pnl.Linear(slope=0.)) objective_mech = pnl.ObjectiveMechanism(monitor=[B]) - ocm = pnl.OptimizationControlMechanism(agent_rep=comp, - state_features=[A.input_port], - objective_mechanism=objective_mech, - function=pnl.GridSearch(), - num_estimates=num_estimates, - control_signals=[control_signal]) + warning_type = None + if num_estimates and not rand_var: + warning_type = UserWarning + warning_msg = f'"\'OptimizationControlMechanism-0\' has \'num_estimates = {num_estimates}\' specified, ' \ + f'but its \'agent_rep\' (\'comp\') has no random variables: ' \ + f'\'RANDOMIZATION_CONTROL_SIGNAL\' will not be created, and num_estimates set to None."' + with pytest.warns(warning_type) as warning: + ocm = pnl.OptimizationControlMechanism(agent_rep=comp, + state_features=[A.input_port], + objective_mechanism=objective_mech, + function=pnl.GridSearch(), + num_estimates=num_estimates, + control_signals=[control_signal]) + if warning_type: + assert repr(warning[5].message.args[0]) == warning_msg comp.add_controller(ocm) - inputs = {A: [[[1.0]]]} comp.run(inputs=inputs, num_trials=2) - if num_estimates is None: + if not num_estimates or not rand_var: assert pnl.RANDOMIZATION_CONTROL_SIGNAL not in comp.controller.control_signals # Confirm no estimates - elif num_estimates==1: - assert comp.controller.control_signals[pnl.RANDOMIZATION_CONTROL_SIGNAL].efferents == []# Confirm no noise - assert np.allclose(comp.simulation_results, - [[np.array([2.25])], [np.array([3.5])], [np.array([4.75])], [np.array([3.])], [np.array([4.25])], [np.array([5.5])]]) - assert np.allclose(comp.results, - [[np.array([1.])], [np.array([1.75])]]) + elif num_estimates: + assert len(comp.controller.control_signals[pnl.RANDOMIZATION_CONTROL_SIGNAL].efferents) == 1 + # noise + + if rand_var: # results for DDM (which has random variables) + assert np.allclose(comp.simulation_results, + [[np.array([2.25])], [np.array([3.5])], [np.array([4.75])], [np.array([3.])], [np.array([4.25])], [np.array([5.5])]]) + assert np.allclose(comp.results, + [[np.array([1.]), np.array([1.1993293])], [np.array([1.]), np.array([3.24637662])]]) + else: # results for ProcessingMechanism (which does not have any random variables) + assert np.allclose(comp.simulation_results, + [[np.array([2.25])], [np.array([3.5])], [np.array([4.75])], [np.array([3.])], [np.array([4.25])], [np.array([5.5])]]) + assert np.allclose(comp.results, + [[np.array([1.])], [np.array([1.75])]]) def test_model_based_ocm_no_simulations(self): A = pnl.ProcessingMechanism(name='A') diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index f8f6b1e1460..45aa0771198 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -666,3 +666,5 @@ def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer ocomp.add_controller(ocm) gv = ocomp.show_graph(output_fmt='source', **show_graph_kwargs) assert gv.strip() == expected_output + + # def test_randomization_control_signal: From 5d2089afef2807c85b6c4879163f9774a4b0d430 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 29 Dec 2021 10:31:36 -0500 Subject: [PATCH 075/285] Tests/control/warnings (#2262) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • component.py docstring mod to **size** * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * - * • test_control.py: - add test_warning_for_add_controller_twice() - add test_warning_for_controller_assigned_to_another_comp() * • test_control.py: - add test_warning_for_replacement_of_controller() * - * - Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- docs/source/AutodiffComposition.rst | 6 +++ docs/source/Composition.rst | 1 + .../CompositionFunctionApproximator.rst | 13 +++-- docs/source/CompositionInterfaceMechanism.rst | 10 ++++ .../source/ParameterEstimationComposition.rst | 9 ++++ .../control/optimizationcontrolmechanism.py | 17 +++++- psyneulink/core/components/ports/inputport.py | 15 +++--- .../core/components/projections/projection.py | 2 +- psyneulink/core/compositions/composition.py | 52 +++++++++++-------- .../parameterestimationcomposition.py | 4 +- psyneulink/core/compositions/showgraph.py | 3 +- tests/composition/test_composition.py | 21 ++++---- tests/composition/test_control.py | 50 +++++++++++++++++- 13 files changed, 156 insertions(+), 47 deletions(-) diff --git a/docs/source/AutodiffComposition.rst b/docs/source/AutodiffComposition.rst index b4f147de430..7f3d25f6266 100644 --- a/docs/source/AutodiffComposition.rst +++ b/docs/source/AutodiffComposition.rst @@ -1,6 +1,12 @@ AutodiffComposition =================== +.. container:: related + + *Related* + + * `Composition_Learning` + .. automodule:: psyneulink.library.compositions.autodiffcomposition :members: :private-members: diff --git a/docs/source/Composition.rst b/docs/source/Composition.rst index 5bad64708e7..bbd3c01f0ca 100644 --- a/docs/source/Composition.rst +++ b/docs/source/Composition.rst @@ -24,6 +24,7 @@ Composition :maxdepth: 1 Pathway + CompositionInterfaceMechanism Scheduling Visualization Report diff --git a/docs/source/CompositionFunctionApproximator.rst b/docs/source/CompositionFunctionApproximator.rst index f9392286136..6964c227df4 100644 --- a/docs/source/CompositionFunctionApproximator.rst +++ b/docs/source/CompositionFunctionApproximator.rst @@ -1,7 +1,6 @@ CompositionFunctionApproximator =============================== - .. container:: subclasses *Subclasses* @@ -11,8 +10,16 @@ CompositionFunctionApproximator RegressionCFA -.. toctree:: - :maxdepth: 2 + | + +.. container:: related + + *Related* + + .. toctree:: + :maxdepth: 1 + + OptimizationControlMechanism .. automodule:: psyneulink.core.compositions.compositionfunctionapproximator :members: diff --git a/docs/source/CompositionInterfaceMechanism.rst b/docs/source/CompositionInterfaceMechanism.rst index 7a9b18233b0..1773f096394 100644 --- a/docs/source/CompositionInterfaceMechanism.rst +++ b/docs/source/CompositionInterfaceMechanism.rst @@ -1,6 +1,16 @@ CompositionInterfaceMechanism ============================= +.. container:: related + + *Related* + + .. toctree:: + :maxdepth: 1 + + Mechanism + Composition + .. automodule:: psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism :members: :private-members: diff --git a/docs/source/ParameterEstimationComposition.rst b/docs/source/ParameterEstimationComposition.rst index b0a50802329..deaf93a9b85 100644 --- a/docs/source/ParameterEstimationComposition.rst +++ b/docs/source/ParameterEstimationComposition.rst @@ -1,6 +1,15 @@ ParameterEstimationComposition ============================== +.. container:: related + + *Related* + + .. toctree:: + :maxdepth: 1 + + OptimizationControlMechanism + .. toctree:: :maxdepth: 2 diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 5b658b6239b..da7e4168eca 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -295,6 +295,21 @@ input to the specified InputPort; that is, the value of which is used as the corresponding value of the OptimizationControlMechanism's `state_feature_values `. + .. note:: + Only the `INPUT ` `Nodes ` of a `nested Composition ` + can shadowed. Therefore, if the Composition that an OptimizationControlMechanism controls contains any + nested Compositions, only its `INPUT ` Nodes can be specified for shadowing in the + **state_features** argument of the OptimizationControlMechanism's constructor. + + .. hint:: + Shadowing the input to a Node of a `nested Composition ` that is not an `INTERNAL + ` Node of that Composition can be accomplished one or of two ways, by: a) assigning it + `INPUT ` as a `required NodeRole ` where it is added to + the nested Composition; and/or b) adding an additional Node to that Composition that shadows the desired one + (this is allowed *within* the *same* Composition), and is assigned as an `OUTPUT ` Node of + that Composition, the `OutputPort` of which which can then be specified in the **state_features** argument of + the OptimizationControlMechanism's constructor (see below). + .. technical_note:: The InputPorts specified as state_features are marked as `internal_only ` = `True`. @@ -307,7 +322,7 @@ `INPUT ` `Node ` of that Composition, and the Mechanism's `primary InputPort ` is used (since in this case the state_feature must correspond to an input to the Composition). If the `agent_rep ` is a `CompositionFunctionApproximator`, then the - Mechanism's `primary OutputPort ` is used (since is the typically usage for specifying an + Mechanism's `primary OutputPort ` is used (since is the typical usage for specifying an InputPort); if the input to the Mechanism is to be shadowed, then its InputPort must be specified explicitly. COMMENT: diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 44d155d5243..bfb01dd4285 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -336,17 +336,18 @@ .. note:: Only InputPorts belonging to Mechanisms in the *same Composition*, or ones that are `INPUT ` - `Nodes ` of a `nested ` can be specified for shadowing, unless the - `allow_probes ` attribute of the `Composition` is set to True. Note also that any - Node that shadows an `INPUT ` `Node ` of the Composition to which it + `Nodes ` of a `nested ` can be specified for shadowing. Note also that + any Node that shadows an `INPUT ` `Node ` of the Composition to which it belongs is itself also assigned the role of `INPUT ` Node. .. hint:: If an InputPort needs to be shadowed that belongs to a Mechanism in a `nested ` that is - not an `INPUT ` `Node ` of that Composition, this can be accomplished as - follows: 1) add a Mechanism to the nested Composition with an InputPort that shadows the one to be - shadowed; 2) specify `OUTPUT ` as a `required_role ` - for that Mechanism; 3) use that Mechanism as the `InputPort specification ` + not an `INPUT ` `Node ` of that Composition, this can be accomplished in + one of two ways: 1) by assigning it `INPUT ` as a `required NodeRole + ` where it is added to the nested Composition; and/or 2) by doing the + following: a) add a Mechanism to the nested Composition with an InputPort that shadows the one to be + shadowed; b) specify `OUTPUT ` as a `required_role ` + for that Mechanism; c) use that Mechanism as the `InputPort specification ` for the shadowing InputPort. .. _InputPort_Compatability_and_Constraints: diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 40fe7d7d67c..65b17b21e53 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -156,7 +156,7 @@ .. _Projection_Specification_Dictionary: - * **Specification dictionary** -- can contain an entry specifying the type of Projection, and/or entries + * **Projection specification dictionary** -- can contain an entry specifying the type of Projection, and/or entries specifying the value of parameters used to instantiate it. These should take the following form: * *PROJECTION_TYPE*: ** -- diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 79ec1e97f7d..fc6af3e06be 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -458,7 +458,7 @@ Second, they can be the receiver of a Projection, as in the case of a MappingProjection that receives a `LearningProjection` used to modify its `matrix ` parameter. Nevertheless, since they define the connections and therefore dependencies among the Composition's Nodes, they determine the structure of its -graph. Subsets of Nodes connected by Projections are often defined as a `Pathway ` as decribed under +graph. Subsets of Nodes connected by Projections can be defined as a `Pathway ` as decribed under `Composition_Pathways` below). .. _Composition_Graph_Projection_Vertices: @@ -469,13 +469,28 @@ Although individual Projections are directed, pairs of Nodes can be connected with Projections in each direction (forming a local `cycle `), and the `AutoAssociativeProjection` class of Projection can even -connect a Node with itself. Projections can also connect the Node(s) of a Composition to one(s) `nested within it -`. In general, these are to the `INPUT ` Nodes and from the `OUTPUT +connect a Node with itself. Projections can also connect the Node(s) of a Composition to one(s) `nested within +it `. In general, these are to the `INPUT ` Nodes and from the `OUTPUT ` Nodes of a `nested Composition `, but if the Composition's `allow_probes ` attribute is not False, then Projections can be received from any Nodes within a nested Composition (see `Probes ` for additional details). A ControlMechanism can also control (i.e., send a `ControlProjection`) to any Node within a nested Composition. +Projections can be specified between `Mechanisms ` before they are added to a Composition. If both +Mechanisms are later added to the same Composition, and the Projection between them is legal for the Composition, +then the Projection between them is added to it and is used during its `execution `. +However, if the Projection is not legal for the Composition (e.g., the Mechanisms are not assigned as `INTERNAL +` `Nodes ` of two different `nested Compositions `), +the Projection will still be associated with the two Mechanisms (i.e., listed in their `afferents +` and `efferents ` attributes, respectively), but it is not +added to the Composition and not used during its execution. + + .. hint:: + Projections that are associated with the `Nodes ` of a Composition but are not in the + Composition itself (and, accordingly, *not* listed it is `projections ` attribute) + can still be visualized using the Composition's `show_graph ` method, by specifying its + **show_projections_not_in_composition** argument as True; Projections not in the Composition appear in red. + .. technical_note:: .. _Composition_Projections_to_CIMs: @@ -2598,7 +2613,7 @@ def input_function(env, result): from psyneulink.core.compositions.showgraph import ShowGraph, INITIAL_FRAME, SHOW_CIM, EXECUTION_SET, SHOW_CONTROLLER from psyneulink.core.globals.context import Context, ContextFlags, handle_external_context from psyneulink.core.globals.keywords import \ - AFTER, ALL, ALLOW_PROBES, ANY, BEFORE, COMPONENT, COMPOSITION, CONTROL, CONTROL_SIGNAL, CONTROLLER, DEFAULT, \ + AFTER, ALL, ALLOW_PROBES, ANY, BEFORE, COMPONENT, CONTROL, CONTROL_SIGNAL, CONTROLLER, DEFAULT, \ FEEDBACK, FUNCTION, HARD_CLAMP, IDENTITY_MATRIX, INPUT, INPUT_PORTS, INPUTS, INPUT_CIM_NAME, \ LEARNED_PROJECTIONS, LEARNING_FUNCTION, LEARNING_MECHANISM, LEARNING_MECHANISMS, LEARNING_PATHWAY, \ MATRIX, MATRIX_KEYWORD_VALUES, MAYBE, \ @@ -3651,10 +3666,9 @@ def __init__( # Controller self.controller = None self._controller_initialization_status = ContextFlags.INITIALIZED + self.enable_controller = enable_controller if controller: self.add_controller(controller) - else: - self.enable_controller = enable_controller self.controller_mode = controller_mode self.controller_time_scale = controller_time_scale self.controller_condition = controller_condition @@ -7599,17 +7613,16 @@ def add_controller(self, controller:ControlMechanism, context=None): return # Warn for request to assign ControlMechanism that is already the controller of another Composition - if hasattr(controller, COMPOSITION) and controller.composition is not self: - warnings.warn(f"{controller} has already been assigned as the {CONTROLLER} " - f"for another {COMPOSITION} ({controller.composition.name}); assignment ignored.") + if hasattr(controller, 'composition') and controller.composition is not self: + warnings.warn(f"'{controller.name}' has already been assigned as the {CONTROLLER} " + f"for '{controller.composition.name}'; assignment to '{self.name}' ignored.") return # Remove existing controller if there is one if self.controller: # Warn if current one is being replaced - if self.prefs.verbosePref: - warnings.warn(f"The existing {CONTROLLER} for {self.name} ({self.controller.name}) " - f"is being replaced by {controller.name}.") + warnings.warn(f"The existing {CONTROLLER} for '{self.name}' ('{self.controller.name}') " + f"is being replaced by '{controller.name}'.") # Remove Projections for old one for proj in self.projections.copy(): if (proj in self.controller.afferents or proj in self.controller.efferents): @@ -7648,15 +7661,12 @@ def add_controller(self, controller:ControlMechanism, context=None): # needs to be set here to insure call at run time (to catch any new nodes that may have been added) self.needs_update_controller = True - # Confirm that controller has input, and if not then disable it - if not (isinstance(self.controller.input_ports, ContentAddressableList) - and self.controller.input_ports): - # If controller was enabled, warn that it has been disabled - if self.enable_controller: - warnings.warn(f"{self.controller.name} for {self.name} has no input_ports, " - f"so controller will be disabled.") - self.enable_controller = False - return + # Warn if controller is enabled but has no inputs + if (self.enable_controller + and not (isinstance(self.controller.input_ports, ContentAddressableList) + and self.controller.input_ports + and self.controller.afferents)): + warnings.warn(f"{self.controller.name} for {self.name} is enabled but has no inputs.") # ADD MODULATORY COMPONENTS ----------------------------------------------------- diff --git a/psyneulink/core/compositions/parameterestimationcomposition.py b/psyneulink/core/compositions/parameterestimationcomposition.py index 339fc99cb67..6dcc8e3658d 100644 --- a/psyneulink/core/compositions/parameterestimationcomposition.py +++ b/psyneulink/core/compositions/parameterestimationcomposition.py @@ -118,8 +118,8 @@ * **objective_function** - specifies a function used to evaluate the `values ` of the `outcome_variables `, according to which combinations of `parameters ` are assessed. The shape of the `variable - ` of the `objective_function (i.e., its first positional argument) must be the same as an - array containing the `value ` of the OutputPort corresponding to each item specified in + ` of the **objective_function** (i.e., its first positional argument) must be the same as + an array containing the `value ` of the OutputPort corresponding to each item specified in `outcome_variables `. * **optimization_function** - specifies the function used to search over values of the `parameters diff --git a/psyneulink/core/compositions/showgraph.py b/psyneulink/core/compositions/showgraph.py index f6620abdf11..b664046f780 100644 --- a/psyneulink/core/compositions/showgraph.py +++ b/psyneulink/core/compositions/showgraph.py @@ -586,7 +586,8 @@ def show_graph(self, show_projections_not_in_composition : bool : default False specifies whether or not to show `Projections ` that are not active in the current - `Composition`; these will display in red. This option is for use in debugging. + `Composition` (and, accordingly, are *not* listed in its `projections ` + attribute); these are shown in red. show_headers : bool : default True specifies whether or not to show headers in the subfields of a Mechanism's node; only takes effect if diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index a4da72f7419..e11f121dcc2 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -24,8 +24,8 @@ from psyneulink.core.components.mechanisms.processing.transfermechanism import TransferMechanism from psyneulink.core.components.ports.inputport import InputPort from psyneulink.core.components.ports.modulatorysignals.controlsignal import ControlSignal, CostFunctions -from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection from psyneulink.core.components.projections.modulatory.controlprojection import ControlProjection +from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection from psyneulink.core.compositions.composition import Composition, CompositionError, NodeRole from psyneulink.core.compositions.pathway import Pathway, PathwayRole from psyneulink.core.globals.context import Context @@ -363,13 +363,13 @@ def test_add_proj_weights_only(self): assert np.allclose(B.parameters.value.get(comp), [[22.4, 29.6]]) assert np.allclose(proj.matrix.base, weights) - test_args = [(None, ([1],[1],[1],[1])), - ('list', ([[0.60276338]],[[0.64589411]],[[0.96366276]])), - ('set', ([[0.60276338]],[[0.64589411]],[[0.96366276]]))] - @pytest.mark.parametrize('projs, expected_matrices', test_args, ids=[x[0] for x in test_args]) - def test_add_multiple_projections_for_nested_compositions(self, projs, expected_matrices): - """Test automatic creation and explicit specification of Projections from outer Composition to multiple - Nodes of a nested Composition, and between Nodes of nested Compositions. + test_args = [(None, ([1],[1],[1],[1]), 3.0), + ('list', ([[0.60276338]],[[0.64589411]],[[0.96366276]]), 2.02947612), + ('set', ([[0.60276338]],[[0.64589411]],[[0.96366276]]), 2.02947612)] + @pytest.mark.parametrize('projs, expected_matrices, expected_result', test_args, ids=[x[0] for x in test_args]) + def test_add_multiple_projections_for_nested_compositions(self, projs, expected_matrices, expected_result): + """Test both automatic creation as well as explicit specification of Projections from outer Composition to + multiple Nodes of a nested Composition, and between Nodes of nested Compositions. """ A = ProcessingMechanism(name='A') @@ -405,8 +405,8 @@ def test_add_multiple_projections_for_nested_compositions(self, projs, expected_ opway = [[X, oprojs, mcomp, Y, C]] ocomp = Composition(pathways=opway, name='OUTER COMPOSITION') - # gv = ocomp.show_graph(output_fmt=source, show_CIM=True, show_node_structure=True) - # assert gv = expected + assert np.allclose(ocomp.run(inputs=[[1.5]]), expected_result) + if not projs: assert (comp1.output_CIM.output_ports[0].efferents[0].matrix.base == comp2.input_CIM.input_ports[0].path_afferents[0].matrix.base == expected_matrices[0]) @@ -6497,6 +6497,7 @@ def test_input_labels_and_results_by_node_and_no_orphaning_of_nested_output_node assert no_such_node_error_msg in str(error_text.value) def test_unnested_PROBE(self): + """Assigning PROBE to a Node should add OutputPort to output_CIM even if Node is not in a nested Composition""" A = ProcessingMechanism(name='A') B = ProcessingMechanism(name='B') C = ProcessingMechanism(name='C') diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 693b04b8c4c..2777c14aeb2 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -20,7 +20,7 @@ class TestControlSpecification: # FIX: OUTSTANDING ISSUES - # When control is specified in a controller for a Mechanism that is not yet a node in the Composition - # it neverhtless gets activated (in call to controller._activate_projections_for_compositions; + # it nevertheless gets activated (in call to controller._activate_projections_for_compositions; # instead, it should either be put in deferred_init or added to node's aux_components attribute def test_add_node_with_control_specified_then_add_controller(self): @@ -286,6 +286,53 @@ def test_partial_deferred_init(self): # Control Signal "deferred_node": Maximizes over the search space consisting of ints 1-5 assert result == [[10]] + def test_warning_for_add_controller_twice(self): + mech = pnl.ProcessingMechanism() + ctlr_1 = pnl.ControlMechanism() + comp = pnl.Composition() + comp.add_node(mech) + comp.add_controller(ctlr_1) + with pytest.warns(UserWarning, match="ControlMechanism-0 has already been assigned as the controller " + "for Composition-0; assignment ignored."): + comp.add_controller(ctlr_1) + + def test_warning_for_controller_assigned_to_another_comp(self): + mech_1 = pnl.ProcessingMechanism() + ctlr_1 = pnl.ControlMechanism() + comp_1 = pnl.Composition() + comp_1.add_node(mech_1) + comp_1.add_controller(ctlr_1) + mech_2 = pnl.ProcessingMechanism() + comp_2 = pnl.Composition() + comp_2.add_node(mech_2) + with pytest.warns(UserWarning, match="'ControlMechanism-0' has already been assigned as the controller " + "for 'Composition-0'; assignment to 'Composition-1' ignored."): + comp_2.add_controller(ctlr_1) + + def test_warning_for_replacement_of_controller(self): + mech = pnl.ProcessingMechanism() + ctlr_1 = pnl.ControlMechanism() + comp = pnl.Composition() + comp.add_node(mech) + comp.add_controller(ctlr_1) + ctlr_2 = pnl.ControlMechanism() + expected_warning = "The existing controller for 'Composition-0' ('ControlMechanism-0') " \ + "is being replaced by 'ControlMechanism-1'." + with pytest.warns(UserWarning) as warning: + comp.add_controller(ctlr_2) + assert expected_warning in repr(warning[0].message.args[0]) + + def test_controller_has_no_input(self): + mech = pnl.ProcessingMechanism() + ctlr = pnl.ControlMechanism() + comp = pnl.Composition() + comp.add_node(mech) + expected_warning = 'ControlMechanism-0 for Composition-0 is enabled but has no inputs.' + with pytest.warns(UserWarning) as warning: + comp.enable_controller = True + comp.add_controller(ctlr) + assert expected_warning in repr(warning[0].message.args[0]) + # FIX: DEPRACATE THIS TEST - IT ALLOWS A COMPOSITION TO EXECUTE WITH A BAD MONITOR FOR CONTROL SPECIFICATION # SUPERCEDED BY test_args_specific_to_ocm outcome_input_ports WHICH TESTS FOR THIS # def test_deferred_objective_mech(self): @@ -665,6 +712,7 @@ def test_transfer_mechanism_and_ocm_variations( else: assert 'a[intercept] ControlSignal' not in ocm.control.names + class TestControlMechanisms: def test_modulation_of_control_signal_intensity_cost_function_MULTIPLICATIVE(self): From 9da53b9de80f7e71c15033a4ec5f9ab7f4478332 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 29 Dec 2021 23:26:03 -0500 Subject: [PATCH 076/285] Fix/comp/proj warnings (#2264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • component.py docstring mod to **size** * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * - * • test_control.py: - add test_warning_for_add_controller_twice() - add test_warning_for_controller_assigned_to_another_comp() * • test_control.py: - add test_warning_for_replacement_of_controller() * - * - * • composition.py - _check_for_unused_projections(): fix crash for projections in deferred_init - _check_controller_initialization_status(): edited warning message - _add_node_aux_components(): add projections in deferred_init to invalid_aux_components * • test_control.py: - add test_add_node_with_controller_spec_and_control_mech_but_not_a_controller() * • test_control.py: = add test_bad_objective_mechanism_spec() Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- .../modulatory/control/controlmechanism.py | 9 ++- .../processing/objectivemechanism.py | 2 +- psyneulink/core/compositions/composition.py | 47 ++++++++++----- tests/composition/test_composition.py | 4 +- tests/composition/test_control.py | 58 ++++++++++++++++--- 5 files changed, 87 insertions(+), 33 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index a7debad33e4..3886991670c 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1385,11 +1385,10 @@ def _validate_params(self, request_set, target_set=None, context=None): validate_monitored_port_spec(self, obj_mech_spec_list) if not isinstance(target_set[OBJECTIVE_MECHANISM], (ObjectiveMechanism, list, bool)): - raise ControlMechanismError("Specification of {} arg for {} ({}) must be an {}" - "or a list of Mechanisms and/or OutputPorts to be monitored for control". - format(OBJECTIVE_MECHANISM, - self.name, target_set[OBJECTIVE_MECHANISM], - ObjectiveMechanism.componentName)) + raise ControlMechanismError(f"Specification of {OBJECTIVE_MECHANISM} arg for '{self.name}' " + f"({target_set[OBJECTIVE_MECHANISM].name}) must be an " + f"{ObjectiveMechanism.componentType} or a list of Mechanisms and/or " + f"OutputPorts to be monitored for control.") if CONTROL in target_set and target_set[CONTROL]: control = target_set[CONTROL] diff --git a/psyneulink/core/components/mechanisms/processing/objectivemechanism.py b/psyneulink/core/components/mechanisms/processing/objectivemechanism.py index f25a190559d..1ce5d403310 100644 --- a/psyneulink/core/components/mechanisms/processing/objectivemechanism.py +++ b/psyneulink/core/components/mechanisms/processing/objectivemechanism.py @@ -498,7 +498,7 @@ class ObjectiveMechanism(ProcessingMechanism_Base): """ - componentType = OBJECTIVE_MECHANISM + componentType = 'ObjectiveMechanism' classPreferenceLevel = PreferenceLevel.SUBTYPE # These will override those specified in TYPE_DEFAULT_PREFERENCES diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index fc6af3e06be..abeb37f00ee 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2613,7 +2613,7 @@ def input_function(env, result): from psyneulink.core.compositions.showgraph import ShowGraph, INITIAL_FRAME, SHOW_CIM, EXECUTION_SET, SHOW_CONTROLLER from psyneulink.core.globals.context import Context, ContextFlags, handle_external_context from psyneulink.core.globals.keywords import \ - AFTER, ALL, ALLOW_PROBES, ANY, BEFORE, COMPONENT, CONTROL, CONTROL_SIGNAL, CONTROLLER, DEFAULT, \ + AFTER, ALL, ALLOW_PROBES, ANY, BEFORE, COMPONENT, COMPOSITION, CONTROL, CONTROL_SIGNAL, CONTROLLER, DEFAULT, \ FEEDBACK, FUNCTION, HARD_CLAMP, IDENTITY_MATRIX, INPUT, INPUT_PORTS, INPUTS, INPUT_CIM_NAME, \ LEARNED_PROJECTIONS, LEARNING_FUNCTION, LEARNING_MECHANISM, LEARNING_MECHANISMS, LEARNING_PATHWAY, \ MATRIX, MATRIX_KEYWORD_VALUES, MAYBE, \ @@ -3788,6 +3788,7 @@ def _analyze_graph(self, context=None): self._create_CIM_ports(context=context) # Call after above so shadow_projections have relevant organization self._update_shadow_projections(context=context) + # FIX: 12/29/21: MOVE TO _update_shadow_projections # Call again to accomodate any changes from _update_shadow_projections self._determine_node_roles(context=context) self._check_for_projection_assignments(context=context) @@ -4338,6 +4339,12 @@ def _add_node_aux_components(self, node, context=None): feedback=proj_spec[1]) del node.aux_components[node.aux_components.index(proj_spec)] + # MODIFIED 12/29/21 NEW: + # # Finally, check for any deferred_init Projections + invalid_aux_components.extend([p for p in node.projections + if p._initialization_status & ContextFlags.DEFERRED_INIT]) + # MODIFIED 12/29/21 END + return invalid_aux_components def _get_invalid_aux_components(self, node): @@ -5838,10 +5845,16 @@ def _check_for_unused_projections(self, context): if isinstance(node, Composition): node._check_for_unused_projections(context) if isinstance(node, Mechanism): - unused_projections.extend([(f"{proj.name} (to '{node.name}' from '{proj.sender.owner.name}')") - for proj in node.afferents if proj not in self.projections]) - unused_projections.extend([(f"{proj.name} (from '{node.name}' to '{proj.receiver.owner.name}')") - for proj in node.efferents if proj not in self.projections]) + for proj in [p for p in node.projections if p not in self.projections]: + proj_deferred = proj._initialization_status & ContextFlags.DEFERRED_INIT + proj_name = proj._name if proj_deferred else proj.name + if proj in node.afferents: + first_item = '' if proj_deferred else f" (to '{node.name}'" + second_item = '' if proj_deferred else f" from '{proj.sender.owner.name}')." + if proj in node.efferents: + first_item = '' if proj_deferred else f" (from '{node.name}'" + second_item = '' if proj_deferred else f" to '{proj.receiver.owner.name}')." + unused_projections.append(f"{proj_name}{first_item}{second_item}") if unused_projections: warning = f"\nThe following Projections were specified but are not being used by Nodes in '{self.name}':" warnings.warn(warning + "\n\t" + "\n\t".join(unused_projections)) @@ -7890,18 +7903,19 @@ def _check_controller_initialization_status(self, context=None): else: owner = component.receiver.owner warnings.warn( - f"The controller of {self.name} has been specified to project to {owner.name}, " - f"but {owner.name} is not in {self.name} or any of its nested Compositions. " - f"This projection will be deactivated until {owner.name} is added to {self.name} " + f"The controller of '{self.name}' has been specified to project to '{owner.name}', " + f"but '{owner.name}' is not in '{self.name}' or any of its nested Compositions. " + f"This projection will be deactivated until '{owner.name}' is added to' {self.name}' " f"in a compatible way." ) elif isinstance(component, Mechanism): warnings.warn( - f"The controller of {self.name} has a specification that includes the Mechanism " - f"{component.name}, but {component.name} is not in {self.name} or any of its " - f"nested Compositions. This Mechanism will be deactivated until {component.name} is " - f"added to {self.name} or one of its nested Compositions in a compatible way." + f"The controller of '{self.name}' has a specification that includes the Mechanism " + f"'{component.name}', but '{component.name}' is not in '{self.name}' or any of its " + f"nested Compositions. This Mechanism will be deactivated until '{component.name}' is " + f"added to '{self.name}' or one of its nested Compositions in a compatible way." ) + assert False, "WARNING MESSAGE" # If Composition is not preparing to execute, allow deferred_inits to persist without warning if context and ContextFlags.PREPARING not in context.execution_phase: @@ -7912,10 +7926,11 @@ def _check_controller_initialization_status(self, context=None): for projection in node.projections: if projection.initialization_status == ContextFlags.DEFERRED_INIT: if isinstance(projection, ControlProjection): - warnings.warn(f"The {projection.receiver.name} parameter of {projection.receiver.owner.name} \n" - f"is specified for control, but {self.name} does not have a controller. Please \n" - f"add a controller to {self.name} or the control specification will be \n" - f"ignored.") + warnings.warn(f"The '{projection.receiver.name}' parameter of " + f"'{projection.receiver.owner.name}' is specified for control, " + f"but the {COMPOSITION} it is in ('{self.name}') does not have a controller; " + f"if a controller is not added to {self.name} " + f"the control specification will be ignored.") def _check_nodes_initialization_status(self, context=None): diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index e11f121dcc2..fe6aec55741 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -568,8 +568,8 @@ def test_unused_projections_warning(self): with pytest.warns(UserWarning) as warning: ocomp = Composition(name='OUTER COMPOSITION',pathways=[comp1, comp2]) ocomp.run() - assert repr(warning[0].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'iCOMP\':\\n\\tMappingProjection from A[OutputPort-0] to C[InputPort-0] (from \'A\' to \'C\')"' - assert repr(warning[1].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'COMP_2\':\\n\\tMappingProjection from A[OutputPort-0] to C[InputPort-0] (to \'C\' from \'A\')"' + assert repr(warning[0].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'iCOMP\':\\n\\tMappingProjection from A[OutputPort-0] to C[InputPort-0] (from \'A\' to \'C\')."' + assert repr(warning[1].message.args[0]) == '"\\nThe following Projections were specified but are not being used by Nodes in \'COMP_2\':\\n\\tMappingProjection from A[OutputPort-0] to C[InputPort-0] (to \'C\' from \'A\')."' class TestPathway: diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 2777c14aeb2..366c8a1266f 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -18,11 +18,6 @@ class TestControlSpecification: # 2) specification of control in controller supercedes any conflicting specification on a node; # 3) order of addition to the composition does not matter (i.e., Principle 2 always applies) - # FIX: OUTSTANDING ISSUES - - # When control is specified in a controller for a Mechanism that is not yet a node in the Composition - # it nevertheless gets activated (in call to controller._activate_projections_for_compositions; - # instead, it should either be put in deferred_init or added to node's aux_components attribute - def test_add_node_with_control_specified_then_add_controller(self): # First add Mechanism with control specification to Composition, # then add controller with NO control specification to Composition @@ -89,14 +84,18 @@ def test_redundant_control_spec_add_controller_in_comp_constructor_then_add_node pnl.ControlProjection( function=pnl.Linear, control_signal_params={ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)})))) - comp = pnl.Composition(controller=pnl.ControlMechanism(control_signals=("drift_rate", ddm))) + expected_warning = "The controller of 'Composition-0' has been specified to project to 'DDM-0', but 'DDM-0' " \ + "is not in 'Composition-0' or any of its nested Compositions. This projection will be " \ + "deactivated until 'DDM-0' is added to' Composition-0' in a compatible way." + with pytest.warns(UserWarning, match=expected_warning): + comp = pnl.Composition(controller=pnl.ControlMechanism(control_signals=("drift_rate", ddm))) comp.add_node(ddm) assert comp.controller.control_signals[0].efferents[0].receiver == ddm.parameter_ports['drift_rate'] assert ddm.parameter_ports['drift_rate'].mod_afferents[0].sender.owner == comp.controller assert comp.controller.control_signals[0].allocation_samples is None def test_redundant_control_spec_add_controller_in_comp_constructor_then_add_node_with_alloc_samples_specified(self): - # First create Composition with controller that has HAS control specification, + # First create Composition with controller that has HAS control specification that includes allocation_samples, # then add Mechanism with control specification to Composition; # Control specification on controller should supercede one on Mechanism (which should be ignored) ddm = pnl.DDM(function=pnl.DriftDiffusionAnalytical( @@ -104,13 +103,34 @@ def test_redundant_control_spec_add_controller_in_comp_constructor_then_add_node pnl.ControlProjection( function=pnl.Linear, control_signal_params={ALLOCATION_SAMPLES: np.arange(0.1, 1.01,0.3)})))) - comp = pnl.Composition(controller=pnl.ControlMechanism(control_signals={ALLOCATION_SAMPLES:np.arange(0.2,1.01, 0.3), - PROJECTIONS:('drift_rate', ddm)})) + expected_warning = "The controller of 'Composition-0' has been specified to project to 'DDM-0', but 'DDM-0' " \ + "is not in 'Composition-0' or any of its nested Compositions. This projection will be " \ + "deactivated until 'DDM-0' is added to' Composition-0' in a compatible way." + with pytest.warns(UserWarning, match=expected_warning): + comp = pnl.Composition(controller=pnl.ControlMechanism(control_signals={ALLOCATION_SAMPLES:np.arange(0.2,1.01, 0.3), + PROJECTIONS:('drift_rate', ddm)})) comp.add_node(ddm) assert comp.controller.control_signals[0].efferents[0].receiver == ddm.parameter_ports['drift_rate'] assert ddm.parameter_ports['drift_rate'].mod_afferents[0].sender.owner == comp.controller assert np.allclose(comp.controller.control[0].allocation_samples(), [0.2, 0.5, 0.8]) + # def test_missing_mech_referenced_by_controller_warning(self): + # mech = pnl.ProcessingMechanism() + # warning_msg_1 = '' + # with pytest.warns(UserWarning) as warning: + # comp = pnl.Composition(controller=pnl.ControlMechanism(objective_mechanism=mech)) + # assert repr(warning[1].message.args[0]) == warning_msg_1 + + def test_bad_objective_mechanism_spec(self): + mech = pnl.ProcessingMechanism() + expected_error = 'Specification of objective_mechanism arg for \'ControlMechanism-0\' ' \ + '(ProcessingMechanism-0) must be an ObjectiveMechanism or a list of Mechanisms ' \ + 'and/or OutputPorts to be monitored for control.' + with pytest.raises(pnl.ControlMechanismError) as error: + comp = pnl.Composition(controller=pnl.ControlMechanism(objective_mechanism=mech)) + error_msg = error.value.error_value + assert expected_error in error_msg + def test_deferred_init(self): # Test to insure controller works the same regardless of whether it is added to a composition before or after # the nodes it connects to @@ -1331,6 +1351,26 @@ def test_control_of_mech_port(self, comp_mode): results = comp.run(inputs=inputs, num_trials=1, execution_mode=comp_mode) assert np.allclose(comp.results, [[[0.375]]]) + @pytest.mark.control + @pytest.mark.composition + def test_add_node_with_controller_spec_and_control_mech_but_not_a_controller(self): + mech = pnl.ProcessingMechanism(name='MECH', function=pnl.Linear(slope=(2, pnl.CONTROL))) + ctl = pnl.ControlMechanism(name='CONTROL MECHANISM') + warning_msg_1 = '"OutputPort (\'ControlSignal-0\') of \'CONTROL MECHANISM\' doesn\'t have any efferent ' \ + 'Projections in \'COMPOSITION\'."' + warning_msg_4 = '"\\nThe following Projections were specified but are not being used by Nodes in ' \ + '\'COMPOSITION\':\\n\\tControlProjection for MECH[slope]"' + warning_msg_5 = '"The \'slope\' parameter of \'MECH\' is specified for control, but the Composition it is in ' \ + '(\'COMPOSITION\') does not have a controller; if a controller is not added to COMPOSITION ' \ + 'the control specification will be ignored."' + with pytest.warns(UserWarning) as warning: + comp = pnl.Composition(name='COMPOSITION', pathways=[ctl]) + comp.add_node(mech) + comp.run() + assert repr(warning[1].message.args[0]) == warning_msg_1 + assert repr(warning[4].message.args[0]) == warning_msg_4 + assert repr(warning[5].message.args[0]) == warning_msg_5 + @pytest.mark.control @pytest.mark.composition @pytest.mark.parametrize("cost, expected, exp_values", [ From ecaf289b4a3ff5587b8b97b9fb79b5929d727e19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Dec 2021 17:42:21 +0000 Subject: [PATCH 077/285] requirements: update pytest-helpers-namespace requirement (#2263) --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 9dc8d8afd90..48bea50167c 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -2,7 +2,7 @@ jupyter<=1.0.0 pytest<6.2.6 pytest-benchmark<3.4.2 pytest-cov<3.0.1 -pytest-helpers-namespace<2021.4.30 +pytest-helpers-namespace<2021.12.30 pytest-profiling<=1.7.0 pytest-pycodestyle<=2.2.0 pytest-pydocstyle<=2.2.0 From 05fcc78efb1a1b7ba898f4ce85a201f8f298f618 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sat, 1 Jan 2022 12:11:17 -0500 Subject: [PATCH 078/285] Fix/ocm/misc (#2267) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * - * • test_control.py: - add test_warning_for_add_controller_twice() - add test_warning_for_controller_assigned_to_another_comp() * • test_control.py: - add test_warning_for_replacement_of_controller() * - * - * • composition.py - _check_for_unused_projections(): fix crash for projections in deferred_init - _check_controller_initialization_status(): edited warning message - _add_node_aux_components(): add projections in deferred_init to invalid_aux_components * • test_control.py: - add test_add_node_with_controller_spec_and_control_mech_but_not_a_controller() * • test_control.py: = add test_bad_objective_mechanism_spec() * • test_control.py: = add test_bad_objective_mechanism_spec() * - * • optimizationcontrolmechanism.py: update figure in docstring * • optimizationcontrolmechanism.py: document state_features attribute * • optimizationcontrolmechanism.py: - docs: - document state_feature_attribute - update figure - implement state property - _update_state_input_ports_for_controller(): fix bug in which assignment of OutputPort as state_feature caused crash * • composition.py: - _get_total_cost_of_control_allocation(): add get_controller to find controller for which self (Composition) is the agent_rep * • show_graph.py - show_graph(): add show_all option * - * - Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- docs/source/_static/Optimization_fig.svg | 3184 ----------------- docs/source/_static/Optimization_fig_BAK.svg | 2556 +++++++++++++ .../control/optimizationcontrolmechanism.py | 30 +- psyneulink/core/compositions/composition.py | 70 +- psyneulink/core/compositions/showgraph.py | 30 +- psyneulink/core/globals/context.py | 2 +- tests/composition/test_control.py | 24 + tests/composition/test_show_graph.py | 50 +- 8 files changed, 2734 insertions(+), 3212 deletions(-) delete mode 100644 docs/source/_static/Optimization_fig.svg create mode 100644 docs/source/_static/Optimization_fig_BAK.svg diff --git a/docs/source/_static/Optimization_fig.svg b/docs/source/_static/Optimization_fig.svg deleted file mode 100644 index 46594b558a0..00000000000 --- a/docs/source/_static/Optimization_fig.svg +++ /dev/null @@ -1,3184 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -A -Optimization - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OptimizationCont - r - olMechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Composition or - - - - - - - - - - - CompositionFunctionApp - r - oximator - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OptimizationFunction - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sea - r - ch_function - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sea - r - ch_space - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - agent_ - r - ep - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (adapt) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - call function - - - - - - - - - - - - - - - - - - - - pass a - r - gument - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Execution - - - - - - - - - - - - - - - - - - - - Update - - - - - - - - - - - featu - r - e values - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Adapt) - - - - - - - - - - - - - - - - Optimize - - - - - - - - - - - cont - r - ol_allocation - - - - - - - - - - - Implement - - - - - - - - - - - cont - r - ol_allocation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - objective_function - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r - etu - r - n/assign value - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - evaluate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - evaluate_agent_ - r - ep - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - outcome - - - - - - - - - - - - - State - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Objective - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Composition Inputs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Monito - r - ed - - - - - - - - - - - - - - - - - - - - - - - - V - alues - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - net_outcome - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - cont - r - ol_allocation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - state_featu - r - es - - - -B - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Model-based - - - - - - - - - - - - - - - - - - - - - - - - Cont - r - ol - - - - - - - - - - - - - - - - - - - - - - Evaluation - - - - - - - - - - - - - - - - - - - - - - - - Processing - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Objective - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - “Model-f - r - ee” - - - - - - - - - - - - - - - - - - - - Features - - - - - - - - - - - - - - - - - - - - Outcome - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Optimization - - - - - - - - - - - Cont - r - ol - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - Simulation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Objective - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - agent_rep - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - agent_rep - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Composition - - - - - - - - - - - Composition - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Optimization - - - - - - - - - - - Cont - r - ol - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Composition - - - - - - - - - - - - - - - - - - - - - - Function - - - - - - - - - - - - - - - - - - - - - Approximator - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Adaptation - - - - - - - - - diff --git a/docs/source/_static/Optimization_fig_BAK.svg b/docs/source/_static/Optimization_fig_BAK.svg new file mode 100644 index 00000000000..5358b4c7e01 --- /dev/null +++ b/docs/source/_static/Optimization_fig_BAK.svg @@ -0,0 +1,2556 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A +Optimization + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Model-based + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cont + r + ol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Evaluation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Processing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Objective + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + “Model-f + r + ee” + + + + + + + + + + + + + + + + + + + + + + + + + + + + Features + + + + + + + + + + + + + + + + + + + + + + + + + + + + Outcome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Optimization + + + + + + + + + + + + + + + Cont + r + ol + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Simulation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Objective + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + agent_rep + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + agent_rep + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Composition + + + + + + + + + + + + + + + Composition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Optimization + + + + + + + + + + + + + + + Cont + r + ol + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Composition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Function + + + + + + + + + + + + + + + + + + + + + Approximator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OptimizationControlMechanism + + + + Composition + + + + + OptimizationFunction + + + search_function + + + search_space + + + agent_rep + + + + call function + + pass argument + + + + Execution + + Update + + feature values + Optimize + + control_allocation + Implement + + control_allocation + + + objective_function + + + + return/assign value + + + + + + + evaluate + + + + + evaluate_agent_rep + State + + + + Objective + Mechanism + + + + + + + Composition Inputs + + + + + Monitored + + + + + + + + + + + + Values + + + + + + + + + net_outcome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + state_features + + + costs + + + + + + + control_allocation + + + outcome + + + +B + + + diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index da7e4168eca..067ceda8445 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1037,8 +1037,14 @@ class OptimizationControlMechanism(ControlMechanism): agent_rep_type : None, COMPOSITION or COMPOSITION_FUNCTION_APPROXIMATOR identifies whether the agent_rep is a `Composition`, a `CompositionFunctionApproximator` or - one of its subclasses, or it has not been assigned (None); see `Agent Representation and Types - of Optimization ` for additional details. + one of its subclasses, or it has not been assigned (None) (see `Agent Representation and Types + of Optimization ` for additional details). + + state_features : List[Mechanism, InputPort, or OutputPort, Projection, or dict] + lists the specifications provided to the **state_features** argument of the OptimizationControlMechanism's + constructor, that are used to generate the inputs to `state_input_ports + ` (see `OptimizationControlMechanism_State_Features` for + additional details). state_feature_values : 2d array the current value of each item of the OptimizationControlMechanism's @@ -1062,6 +1068,12 @@ class OptimizationControlMechanism(ControlMechanism): ` in a given `OptimizationControlMechanism_State` (see `Outcome ` for additional details). + COMMENT: + state : ndarray + lists the values of the current state -- a concatenation of the state_feature_values and control_allocation + following the last execution of the `agent_rep `. + COMMENT + num_estimates : int determines the number independent runs of `agent_rep ` (i.e., calls to `evaluate_agent_rep `) used to estimate the `net_outcome @@ -1651,7 +1663,8 @@ def _get_all_input_nodes(comp): # Ensure that all InputPorts shadowed by specified state_input_ports # are in agent_rep or one of its nested Compositions invalid_state_features = [input_port for input_port in self.state_input_ports - if (not (input_port.shadow_inputs.owner in + if (input_port.shadow_inputs + and not (input_port.shadow_inputs.owner in list(comp.nodes) + [n[0] for n in comp._get_nested_nodes()]) and (not [input_port.shadow_inputs.owner.composition is x for x in comp._get_nested_compositions() @@ -1666,9 +1679,11 @@ def _get_all_input_nodes(comp): # Ensure that all InputPorts shadowed by specified state_input_ports # reference INPUT Nodes of agent_rep or of a nested Composition invalid_state_features = [input_port for input_port in self.state_input_ports - if (not (input_port.shadow_inputs.owner in _get_all_input_nodes(self.agent_rep)) + if (input_port.shadow_inputs + and not (input_port.shadow_inputs.owner + in _get_all_input_nodes(self.agent_rep)) and (isinstance(input_port.shadow_inputs.owner, - CompositionInterfaceMechanism) + CompositionInterfaceMechanism) and not (input_port.shadow_inputs.owner.composition in [nested_comp for nested_comp in comp._get_nested_compositions() if nested_comp in comp.get_nodes_by_role(NodeRole.INPUT)])))] @@ -2345,6 +2360,7 @@ def _gen_llvm_output_port_parse_variable(self, ctx, builder, params, context, va builder.store(builder.load(val_ptr), dest_ptr) return oport_input + # Deprecated - this is now a Parameter # @property # def state_feature_values(self): # if hasattr(self.agent_rep, 'model_based_optimizer') and self.agent_rep.model_based_optimizer is self: @@ -2415,6 +2431,10 @@ def num_state_input_ports(self): except: return 0 + @property + def state(self): + return self.state_feature_values + self.control_allocation + @property def _model_spec_parameter_blacklist(self): # default_variable is hidden in constructor arguments, diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index abeb37f00ee..ce064414ff3 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -5039,6 +5039,7 @@ def _create_CIM_ports(self, context=None): if x in cim_prt_tpl), len(self.nodes))) + # KDM 4/3/20: should reevluate this some time - is it # acceptable to consider _update_default_variable as # happening outside of this normal context? This is here as @@ -5049,6 +5050,7 @@ def _create_CIM_ports(self, context=None): # otherwise, CIM ports will not be initialized properly orig_eid = context.execution_id context.execution_id = None + context_string = context.string new_default_variable = [ deepcopy(input_port.defaults.value) @@ -5065,6 +5067,7 @@ def _create_CIM_ports(self, context=None): # no input ports in CIM, so assume Composition is blank context.execution_id = orig_eid + context.string = context_string # verify there is exactly one automatically instantiated input port for each automatically instantiated # output port @@ -5134,7 +5137,10 @@ def try_assigning_as_probe(node, role, comp): # Check if Node is an INPUT or INTERNAL if any(role for role in comp.nodes_to_roles[node] if role in {NodeRole.INPUT, NodeRole.INTERNAL}): comp._add_required_node_role(node, NodeRole.PROBE) - self._analyze_graph() + # Ignore warning since a Projection to the PROBE will not yet have been instantiated + # self._analyze_graph(context=Context(string='IGNORE_NO_AFFERENTS_WARNING')) + self._analyze_graph(context=Context(source=ContextFlags.COMPOSITION, + string='IGNORE_NO_AFFERENTS_WARNING')) return # Failed to assign node as PROBE, so get ControlMechanisms that may be trying to monitor it @@ -5821,7 +5827,7 @@ def _check_for_projection_assignments(self, context=None): projections.append(node) continue - if context.source != ContextFlags.INITIALIZING: + if context.source != ContextFlags.INITIALIZING and context.string != 'IGNORE_NO_AFFERENTS_WARNING': for input_port in node.input_ports: if input_port.require_projection_in_composition and not input_port.path_afferents: warnings.warn(f"{InputPort.__name__} ('{input_port.name}') of '{node.name}' " @@ -7646,6 +7652,17 @@ def add_controller(self, controller:ControlMechanism, context=None): # Assign mutual references between Composition and controller controller.composition = self self.controller = controller + + # # MODIFIED 12/30/21 NEW: FIX: THIS IS NOT CORRECT, BECAUSE WITH REGARD TO EXECUTION SEQUENCING, + # # CONTROLLER IS NOT THE CONTROLLER OF THE AGENT_REP, + # # IT JUST EXECUTES IT. + # # Deal with agent_rep of controller that is in a nested Composition + # if (hasattr(self.controller, AGENT_REP) + # and self.controller.agent_rep != self + # and self.controller.agent_rep in self._get_nested_compositions()): + # self.controller.agent_rep.controller = self.controller + # MODIFIED 12/30/21 END + # Having controller in nodes is not currently supported (due to special handling of scheduling/execution); # its NodeRole assignment is handled directly by the get_nodes_by_role and get_roles_by_node methods. # self._add_node_role(controller, NodeRole.CONTROLLER) @@ -7787,6 +7804,20 @@ def _get_control_signals_for_composition(self): control_signal_specs.extend(node._get_parameter_port_deferred_init_control_specs()) return control_signal_specs + # def _get_controller(comp, context=None): + # """Get controller for which the current Composition is an agent_rep. + # Recursively search enclosing Compositions for controller if self does not have one. + # Use context.composition if there is no controller. + # This is needed for agent_rep that is nested within the Composition to which the controller belongs. + # """ + # context = context or Context(source=ContextFlags.COMPOSITION, composition=None) + # if comp.controller: + # return comp.controller + # elif context.composition: + # return context.composition._get_controller(context) + # else: + # assert False, f"PROGRAM ERROR: Can't find controller for {comp.name}." + def reshape_control_signal(self, arr): current_shape = np.shape(arr) @@ -7908,6 +7939,8 @@ def _check_controller_initialization_status(self, context=None): f"This projection will be deactivated until '{owner.name}' is added to' {self.name}' " f"in a compatible way." ) + # FIX: It seems this may never get called, as any specification of a Mechanism in the constructor + # for a ControlMechanism automatically instantiates a Projection that triggers the warning above. elif isinstance(component, Mechanism): warnings.warn( f"The controller of '{self.name}' has a specification that includes the Mechanism " @@ -8001,28 +8034,43 @@ def _get_total_cost_of_control_allocation(self, control_allocation, context, run total_cost = 0. if control_allocation is not None: # using "is not None" in case the control allocation is 0. - base_control_allocation = self.reshape_control_signal(self.controller.parameters.value._get(context)) + def get_controller(comp): + """Get controller for which the current Composition is an agent_rep. + Recursively search enclosing Compositions for controller if self does not have one. + Use context.composition to find controller. + This is needed for agent_rep that is nested within the Composition to which the controller belongs. + """ + if comp.controller: + return comp.controller + elif context.composition: + return get_controller(context.composition) + else: + assert False, f"PROGRAM ERROR: Can't find controller for {self.name}." + + controller = get_controller(self) + base_control_allocation = self.reshape_control_signal(controller.parameters.value._get(context)) candidate_control_allocation = self.reshape_control_signal(control_allocation) # Get reconfiguration cost for candidate control signal reconfiguration_cost = 0. - if callable(self.controller.compute_reconfiguration_cost): - reconfiguration_cost = self.controller.compute_reconfiguration_cost([candidate_control_allocation, + if callable(controller.compute_reconfiguration_cost): + reconfiguration_cost = controller.compute_reconfiguration_cost([candidate_control_allocation, base_control_allocation]) - self.controller.reconfiguration_cost.set(reconfiguration_cost, context) + controller.reconfiguration_cost.set(reconfiguration_cost, context) # Apply candidate control signal - self.controller._apply_control_allocation(candidate_control_allocation, + controller._apply_control_allocation(candidate_control_allocation, context=context, runtime_params=runtime_params, ) # Get control signal costs - other_costs = self.controller.parameters.costs._get(context) or [] + other_costs = controller.parameters.costs._get(context) or [] all_costs = convert_to_np_array(other_costs + [reconfiguration_cost]) # Compute a total for the candidate control signal(s) - total_cost = self.controller.combine_costs(all_costs) + total_cost = controller.combine_costs(all_costs) + return total_cost # endregion CONTROL @@ -11146,6 +11194,7 @@ def _all_nodes(self): # ****************************************************************************************************************** def show_graph(self, + show_all=False, show_node_structure=False, show_nested=NESTED, show_nested_args=ALL, @@ -11161,7 +11210,8 @@ def show_graph(self, output_fmt='pdf', context=None): - return self._show_graph(show_node_structure=show_node_structure, + return self._show_graph(show_all=show_all, + show_node_structure=show_node_structure, show_nested=show_nested, show_nested_args=show_nested_args, show_cim=show_cim, diff --git a/psyneulink/core/compositions/showgraph.py b/psyneulink/core/compositions/showgraph.py index b664046f780..26bb36fc010 100644 --- a/psyneulink/core/compositions/showgraph.py +++ b/psyneulink/core/compositions/showgraph.py @@ -470,6 +470,7 @@ def __init__(self, @tc.typecheck @handle_external_context(source=ContextFlags.COMPOSITION) def show_graph(self, + show_all:bool=False, show_node_structure:tc.any(bool, tc.enum(VALUES, LABELS, FUNCTIONS, MECH_FUNCTION_PARAMS, PORT_FUNCTION_PARAMS, ROLES, ALL))=False, show_nested:tc.optional(tc.any(bool,int,dict,tc.enum(NESTED, INSET)))=NESTED, @@ -485,9 +486,11 @@ def show_graph(self, active_items=None, output_fmt:tc.optional(tc.enum('pdf','gv','jupyter','gif'))='pdf', context=None, + *args, **kwargs): """ show_graph( \ + show_all=False, \ show_node_structure=False, \ show_nested=NESTED, \ show_nested_args=ALL, \ @@ -513,6 +516,10 @@ def show_graph(self, Arguments --------- + show_all : bool : default False + if False, defer to specification of all other arguments; if True, override all show_XXX arguments, + automatically specifying them with their most informative settings. + show_node_structure : bool, VALUES, LABELS, FUNCTIONS, MECH_FUNCTION_PARAMS, PORT_FUNCTION_PARAMS, ROLES, \ or ALL : default False show a detailed representation of each `Mechanism ` in the graph, including its `Ports `; @@ -667,6 +674,16 @@ def show_graph(self, # ASSIGN ATTRIBUTES PASSED TO NESTED COMPOSITIONS ----------------------------------------------- + if show_all: + show_node_structure=ALL + show_nested=NESTED + show_nested_args=ALL + show_cim=True + show_controller=True + show_learning=ALL + show_headers=True + show_projections_not_in_composition=True + # Assign node_struct_arg based on show_node_structure ~~~~~~~~~~~~~~~~~~~~~~~~~ # Argument values used to call Mechanism._show_structure() if isinstance(show_node_structure, (list, tuple, set)): @@ -1643,10 +1660,10 @@ def _assign_controller_components(self, ctl_proj_rcvr = ctl_proj.receiver # If receiver is a parameter_CIM if isinstance(ctl_proj_rcvr.owner, CompositionInterfaceMechanism): - # PATCH 6/7/20 to deal with ControlProjections across more than one level of nesting: + # Deal with ControlProjections across more than one level of nesting: rcvr_comp = ctl_proj_rcvr.owner.composition def find_rcvr_comp(r, c, l): - """Find deepest enclosing composition within range of num_nesting_levels""" + """Find deepest Composition within c that encloses r within range of num_nesting_levels of c""" if (self.num_nesting_levels is not None and l > self.num_nesting_levels): return c, l elif r in c.nodes: @@ -1674,26 +1691,17 @@ def find_rcvr_comp(r, c, l): rcvr_comp = enclosing_comp else: rcvr_comp = enclosing_comp - # PATCH 6/6/20 END - # PATCH 6/6/20: # if show_cim and show_nested is NESTED: if show_cim and project_to_node: - # PATCH 6/6/20 END # Use Composition's parameter_CIM port ctl_proj_rcvr_owner = ctl_proj_rcvr.owner - # PATCH 6/6/20: - # elif show_nested is NESTED: elif project_to_node: - # PATCH 6/6/20 END ctl_proj_rcvr = self._trace_receivers_for_terminal_receiver(ctl_proj_rcvr) ctl_proj_rcvr_owner = ctl_proj_rcvr.owner else: # Use Composition if show_cim is False - # PATCH 6/6/20: - # ctl_proj_rcvr_owner = ctl_proj_rcvr.owner.composition ctl_proj_rcvr_owner = rcvr_comp - # PATCH 6/6/20 END # In all other cases, use Port (either ParameterPort of a Mech, or parameter_CIM for nested comp) else: ctl_proj_rcvr_owner = ctl_proj_rcvr.owner diff --git a/psyneulink/core/globals/context.py b/psyneulink/core/globals/context.py index a9a52284d63..250ff7bf63b 100644 --- a/psyneulink/core/globals/context.py +++ b/psyneulink/core/globals/context.py @@ -498,7 +498,7 @@ def add_to_string(self, string): if self.string is None: self.string = string else: - self.string = '{0} {1} {2}'.format(self.string, SEPARATOR_BAR, string) + self.string = f'{self.string} {SEPARATOR_BAR} {string}' def _change_flags(self, *flags, operation=lambda attr, blank_flag, *flags: NotImplemented): # split by flag type to avoid extra costly binary operations on enum flags diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 366c8a1266f..3fb47187d4d 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -2679,6 +2679,30 @@ def test_input_CIM_assignment(self, comp_mode): # -7 ((5*-1)+(-2*1)) assert np.allclose(results, [[7]]) + @pytest.mark.control + @pytest.mark.composition + def test_nested_composition_as_agent_rep(self): + I = pnl.ProcessingMechanism(name='I') + icomp = pnl.Composition(nodes=I, name='INNER COMP') + + A = pnl.ProcessingMechanism(name='A') + B = pnl.ProcessingMechanism(name='B') + C = pnl.ProcessingMechanism(name='C') + mcomp = pnl.Composition(pathways=[[A,B,C], icomp], + name='MIDDLE COMP') + ocomp = pnl.Composition(nodes=[mcomp], name='OUTER COMP') + ocm = pnl.OptimizationControlMechanism(name='OCM', + agent_rep=mcomp, # Nested Composition as agent_rep + state_features=I.input_port, + objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), + allow_probes=True, + function=pnl.GridSearch(), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I), + allocation_samples=[10, 20, 30])) + ocomp.add_controller(ocm) + # FIX: CRASHES IN composition._get_total_cost_of_control_allocation() + # ocomp.run() + class TestSampleIterator: diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index 45aa0771198..d98ae465d06 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -3,6 +3,7 @@ from psyneulink.core.components.functions.nonstateful.learningfunctions import BackPropagation from psyneulink.core.components.functions.nonstateful.transferfunctions import Linear +from psyneulink.core.components.functions.nonstateful.optimizationfunctions import GridSearch from psyneulink.core.components.functions.stateful.memoryfunctions import DictionaryMemory from psyneulink.core.components.functions.stateful.memoryfunctions import STORAGE_PROB from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import ControlMechanism @@ -667,4 +668,51 @@ def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer gv = ocomp.show_graph(output_fmt='source', **show_graph_kwargs) assert gv.strip() == expected_output - # def test_randomization_control_signal: + # def test_show_graph_for_nested_composition_as_agent_rep(self): + # """Note: this is the same as test_control/test_nested_composition_as_agent_rep but with show_graph()""" + # I = ProcessingMechanism(name='I') + # icomp = Composition(nodes=I, name='INNER COMP') + # + # A = ProcessingMechanism(name='A') + # B = ProcessingMechanism(name='B') + # C = ProcessingMechanism(name='C') + # mcomp = Composition(pathways=[[A,B,C], icomp], + # name='MIDDLE COMP') + # ocomp = Composition(nodes=[mcomp], name='OUTER COMP') + # ocm = OptimizationControlMechanism(name='OCM', + # agent_rep=mcomp, # Nested Composition as agent_rep + # state_features=I.input_port, + # objective_mechanism=ObjectiveMechanism(monitor=[B]), + # allow_probes=True, + # function=GridSearch(), + # control_signals=ControlSignal(modulates=(SLOPE,I), + # allocation_samples=[10, 20, 30])) + # ocomp.add_controller(ocm) + # # FIX: THE FOLLOWING IS UNSTABLE; SOMETIMES RENDERS CORRECTLY, BUT SOMETIMES INCORRECTLY SHOWS EDGE + # # FROM ocm.output_CIM.input_port -> mcomp.parameter_CIM.output_port + # # ??SAME BUG AS IN MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c + # expected_gv_correct = 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_MIDDLE COMP_INPUT_CIM_A_InputPort-0
Identity()
=[0.]
INPUT_CIM_MIDDLE COMP_INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
OUTER COMP Input_CIM

Identity()
=None
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_MIDDLE COMP_INPUT_CIM_A_InputPort-0" -> "MIDDLE COMP INPUT_CIM":"InputPort-INPUT_CIM_A_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_MIDDLE COMP_INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0" -> "MIDDLE COMP INPUT_CIM":"InputPort-INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM

Identity()
=None
InputPorts
OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_B_OutputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_C_OutputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=red penwidth=1 rank=same shape=plaintext]\n\t"MIDDLE COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_B_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_B_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"MIDDLE COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_C_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_C_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"MIDDLE COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tOCM:"OutputPort-I[slope] ControlSignal" -> "MIDDLE COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_I_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"ObjectiveMechanism-0" [label=<
OUTCOME
Linear(intercept=None, slope=None)
=[0.]
OutputPorts
Mechanism:
ObjectiveMechanism-0

CONTROLLER_OBJECTIVE,INTERNAL
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[[0.]]
ParameterPorts
offset
Linear(intercept=None, slope=None)
=[0.]
scale
Linear(intercept=None, slope=None)
=[1.]
InputPorts
Value of B [OutputPort-0]
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"ObjectiveMechanism-0":"OutputPort-OUTCOME" -> OCM:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"MIDDLE COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_B_OutputPort-0" -> "ObjectiveMechanism-0":"InputPort-Value of B [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_MIDDLE COMP_INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0" -> OCM:"InputPort-Shadowed input of I" [label="" arrowhead=normal color=purple penwidth=1]\n\tOCM [label=<
I[slope] ControlSignal
TransferWithCosts(combine_costs_fct_add_param=None, intensity_cost_fct_mult_param=None, intensity_cost_fct_add_param=None, duration_cost_fct_mult_param=None, adjustment_cost_fct_mult_param=None, duration_cost_fct_add_param=None, adjustment_cost_fct_add_param=None, transfer_fct_add_param=None, transfer_fct_mult_param=None, combine_costs_fct_mult_param=None)
=[1.]
OutputPorts
Mechanism:
OCM

CONTROLLER
Always()
GridSearch(max_iterations=None, seed=-1)
=[[1.]]
ParameterPorts
seed
Linear(intercept=None, slope=None)
=[-1.]
InputPorts
OUTCOME
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
Shadowed input of I
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_MIDDLE COMP" {\n\t\tgraph [label="MIDDLE COMP" 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\tA [label=<
OutputPort-0
Linear(intercept=None, slope=None)
=[0.]
OutputPorts
Mechanism:
A

INPUT,ORIGIN
Linear(intercept=None, slope=None)
=[[0.]]
ParameterPorts
intercept
Linear(intercept=None, slope=None)
=[0.]
slope
Linear(intercept=None, slope=None)
=[1.]
InputPorts
InputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tB [label=<
OutputPort-0
Linear(intercept=None, slope=None)
=[0.]
OutputPorts
Mechanism:
B

PROBE,INTERNAL
Linear(intercept=None, slope=None)
=[[0.]]
ParameterPorts
intercept
Linear(intercept=None, slope=None)
=[0.]
slope
Linear(intercept=None, slope=None)
=[1.]
InputPorts
InputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=black penwidth=1 rank=same shape=plaintext]\n\t\tA:"OutputPort-OutputPort-0" -> B:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tB:"OutputPort-OutputPort-0" -> C:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"MIDDLE COMP INPUT_CIM" [label=<
INPUT_CIM_A_InputPort-0
Identity()
=[0.]
INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
MIDDLE COMP Input_CIM

Identity()
=None
InputPorts
INPUT_CIM_A_InputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"MIDDLE COMP INPUT_CIM":"OutputPort-INPUT_CIM_A_InputPort-0" -> A:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"MIDDLE COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_I_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"MIDDLE COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_I_slope
TransferWithCosts(combine_costs_fct_add_param=None, intensity_cost_fct_mult_param=None, intensity_cost_fct_add_param=None, duration_cost_fct_mult_param=None, adjustment_cost_fct_mult_param=None, duration_cost_fct_add_param=None, adjustment_cost_fct_add_param=None, combine_costs_fct_mult_param=None)
=[1.]
OutputPorts
Mechanism:
MIDDLE COMP Parameter_CIM

Identity()
=[[0.]\n [1.]]
InputPorts
PARAMETER_CIM_I_slope
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[1.]
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"MIDDLE COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_I_slope" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_I_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"MIDDLE COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_B_OutputPort-0
Identity()
=[0.]
OUTPUT_CIM_C_OutputPort-0
Identity()
=[0.]
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
MIDDLE COMP Output_CIM

Identity()
=None
InputPorts
OUTPUT_CIM_B_OutputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
OUTPUT_CIM_C_OutputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tB:"OutputPort-OutputPort-0" -> "MIDDLE COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_B_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tC:"OutputPort-OutputPort-0" -> "MIDDLE COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_C_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_I_OutputPort-0" -> "MIDDLE COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tC [label=<
OutputPort-0
Linear(intercept=None, slope=None)
=[0.]
OutputPorts
Mechanism:
C

TERMINAL,OUTPUT
Linear(intercept=None, slope=None)
=[[0.]]
ParameterPorts
intercept
Linear(intercept=None, slope=None)
=[0.]
slope
Linear(intercept=None, slope=None)
=[1.]
InputPorts
InputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tsubgraph "cluster_INNER COMP" {\n\t\t\tgraph [label="INNER COMP" 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"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_I_InputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
INNER COMP Input_CIM

Identity()
=None
InputPorts
INPUT_CIM_I_InputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_I_InputPort-0" -> I:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_I_slope
TransferWithCosts(combine_costs_fct_add_param=None, intensity_cost_fct_mult_param=None, intensity_cost_fct_add_param=None, duration_cost_fct_mult_param=None, adjustment_cost_fct_mult_param=None, duration_cost_fct_add_param=None, adjustment_cost_fct_add_param=None, combine_costs_fct_mult_param=None)
=[1.]
OutputPorts
Mechanism:
INNER COMP Parameter_CIM

Identity()
=None
InputPorts
PARAMETER_CIM_I_slope
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[1.]
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_I_slope" -> I:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_I_OutputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
INNER COMP Output_CIM

Identity()
=None
InputPorts
OUTPUT_CIM_I_OutputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t\tI:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_I_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\tI [label=<
OutputPort-0
Linear(intercept=None, slope=None)
=[0.]
OutputPorts
Mechanism:
I

INPUT,SINGLETON,TERMINAL,OUTPUT,ORIGIN
Linear(intercept=None, slope=None)
=[[0.]]
ParameterPorts
intercept
Linear(intercept=None, slope=None)
=[0.]
slope
Linear(intercept=None, slope=None)
=[1.]
InputPorts
InputPort-0
LinearCombination(scale=None, offset=None, exponents=None, weights=None)
=[0.]
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=brown\n\t\t\tlabel="INNER COMP"\n\t\t}\n\t\tcolor=brown\n\t\tlabel="MIDDLE COMP"\n\t}\n}' + # expected_gv_incorrect = 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_MIDDLE COMP_INPUT_CIM_A_InputPort-0
Identity()
=[0.]
INPUT_CIM_MIDDLE COMP_INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
OUTER COMP Input_CIM

Identity()
=None
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_MIDDLE COMP_INPUT_CIM_A_InputPort-0" -> "MIDDLE COMP INPUT_CIM":"InputPort-INPUT_CIM_A_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_MIDDLE COMP_INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0" -> "MIDDLE COMP INPUT_CIM":"InputPort-INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM

Identity()
=None
InputPorts
OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_B_OutputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_C_OutputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=red penwidth=1 rank=same shape=plaintext]\n\t"MIDDLE COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_B_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_B_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"MIDDLE COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_C_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_C_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"MIDDLE COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_MIDDLE COMP_OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tOCM:"OutputPort-I[slope] ControlSignal" -> "MIDDLE COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_I_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"ObjectiveMechanism-0" [label=<
OUTCOME
Linear(slope=None, intercept=None)
=[0.]
OutputPorts
Mechanism:
ObjectiveMechanism-0

INTERNAL,CONTROLLER_OBJECTIVE
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[[0.]]
ParameterPorts
offset
Linear(slope=None, intercept=None)
=[0.]
scale
Linear(slope=None, intercept=None)
=[1.]
InputPorts
Value of B [OutputPort-0]
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"ObjectiveMechanism-0":"OutputPort-OUTCOME" -> OCM:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"MIDDLE COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_B_OutputPort-0" -> "ObjectiveMechanism-0":"InputPort-Value of B [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_MIDDLE COMP_INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0" -> OCM:"InputPort-Shadowed input of I" [label="" arrowhead=normal color=purple penwidth=1]\n\tOCM [label=<
I[slope] ControlSignal
TransferWithCosts(duration_cost_fct_add_param=None, intensity_cost_fct_add_param=None, duration_cost_fct_mult_param=None, combine_costs_fct_add_param=None, adjustment_cost_fct_add_param=None, combine_costs_fct_mult_param=None, adjustment_cost_fct_mult_param=None, transfer_fct_add_param=None, intensity_cost_fct_mult_param=None, transfer_fct_mult_param=None)
=[1.]
OutputPorts
Mechanism:
OCM

CONTROLLER
Always()
GridSearch(max_iterations=None, seed=-1)
=[[1.]]
ParameterPorts
seed
Linear(slope=None, intercept=None)
=[-1.]
InputPorts
OUTCOME
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
Shadowed input of I
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_MIDDLE COMP" {\n\t\tgraph [label="MIDDLE COMP" 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\tA [label=<
OutputPort-0
Linear(slope=None, intercept=None)
=[0.]
OutputPorts
Mechanism:
A

INPUT,ORIGIN
Linear(slope=None, intercept=None)
=[[0.]]
ParameterPorts
intercept
Linear(slope=None, intercept=None)
=[0.]
slope
Linear(slope=None, intercept=None)
=[1.]
InputPorts
InputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tB [label=<
OutputPort-0
Linear(slope=None, intercept=None)
=[0.]
OutputPorts
Mechanism:
B

PROBE,INTERNAL
Linear(slope=None, intercept=None)
=[[0.]]
ParameterPorts
intercept
Linear(slope=None, intercept=None)
=[0.]
slope
Linear(slope=None, intercept=None)
=[1.]
InputPorts
InputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=black penwidth=1 rank=same shape=plaintext]\n\t\tA:"OutputPort-OutputPort-0" -> B:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tB:"OutputPort-OutputPort-0" -> C:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"MIDDLE COMP INPUT_CIM" [label=<
INPUT_CIM_A_InputPort-0
Identity()
=[0.]
INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
MIDDLE COMP Input_CIM

Identity()
=None
InputPorts
INPUT_CIM_A_InputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"MIDDLE COMP INPUT_CIM":"OutputPort-INPUT_CIM_A_InputPort-0" -> A:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"MIDDLE COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_I_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_I_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"MIDDLE COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_I_slope
TransferWithCosts(duration_cost_fct_add_param=None, intensity_cost_fct_add_param=None, duration_cost_fct_mult_param=None, combine_costs_fct_add_param=None, adjustment_cost_fct_add_param=None, combine_costs_fct_mult_param=None, adjustment_cost_fct_mult_param=None, intensity_cost_fct_mult_param=None)
=[1.]
OutputPorts
Mechanism:
MIDDLE COMP Parameter_CIM

Identity()
=[[0.]\n [1.]]
InputPorts
PARAMETER_CIM_I_slope
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[1.]
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"MIDDLE COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_I_slope" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_I_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"MIDDLE COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_B_OutputPort-0
Identity()
=[0.]
OUTPUT_CIM_C_OutputPort-0
Identity()
=[0.]
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
MIDDLE COMP Output_CIM

Identity()
=None
InputPorts
OUTPUT_CIM_B_OutputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
OUTPUT_CIM_C_OutputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tB:"OutputPort-OutputPort-0" -> "MIDDLE COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_B_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tC:"OutputPort-OutputPort-0" -> "MIDDLE COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_C_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_I_OutputPort-0" -> "MIDDLE COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_I_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tC [label=<
OutputPort-0
Linear(slope=None, intercept=None)
=[0.]
OutputPorts
Mechanism:
C

TERMINAL,OUTPUT
Linear(slope=None, intercept=None)
=[[0.]]
ParameterPorts
intercept
Linear(slope=None, intercept=None)
=[0.]
slope
Linear(slope=None, intercept=None)
=[1.]
InputPorts
InputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tsubgraph "cluster_INNER COMP" {\n\t\t\tgraph [label="INNER COMP" 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"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_I_InputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
INNER COMP Input_CIM

Identity()
=None
InputPorts
INPUT_CIM_I_InputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_I_InputPort-0" -> I:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_I_slope
TransferWithCosts(duration_cost_fct_add_param=None, intensity_cost_fct_add_param=None, duration_cost_fct_mult_param=None, combine_costs_fct_add_param=None, adjustment_cost_fct_add_param=None, combine_costs_fct_mult_param=None, adjustment_cost_fct_mult_param=None, intensity_cost_fct_mult_param=None)
=[1.]
OutputPorts
Mechanism:
INNER COMP Parameter_CIM

Identity()
=None
InputPorts
PARAMETER_CIM_I_slope
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[1.]
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_I_slope" -> I:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_I_OutputPort-0
Identity()
=[0.]
OutputPorts
Mechanism:
INNER COMP Output_CIM

Identity()
=None
InputPorts
OUTPUT_CIM_I_OutputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t\tI:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_I_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\tI [label=<
OutputPort-0
Linear(slope=None, intercept=None)
=[0.]
OutputPorts
Mechanism:
I

ORIGIN,INPUT,TERMINAL,SINGLETON,OUTPUT
Linear(slope=None, intercept=None)
=[[0.]]
ParameterPorts
intercept
Linear(slope=None, intercept=None)
=[0.]
slope
Linear(slope=None, intercept=None)
=[1.]
InputPorts
InputPort-0
LinearCombination(scale=None, weights=None, offset=None, exponents=None)
=[0.]
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=brown\n\t\t\tlabel="INNER COMP"\n\t\t}\n\t\tcolor=brown\n\t\tlabel="MIDDLE COMP"\n\t}\n}' + # gv = ocomp.show_graph(output_fmt='source', show_cim=True, show_node_structure=ALL) + # try: + # assert repr(gv) == expected_gv_correct + # except AssertionError: + # try: + # assert repr(gv) == expected_gv_incorrect + # except AssertionError: + # try: + # assert gv == expected_gv_correct + # except AssertionError: + # try: + # assert gv == expected_gv_incorrect + # except AssertionError: + # try: + # assert repr(gv) == repr(expected_gv_correct) + # except AssertionError: + # try: + # assert repr(gv) == repr(expected_gv_incorrect) + # except AssertionError: + # try: + # assert gv == repr(expected_gv_correct) + # except AssertionError: + # assert gv == repr(expected_gv_incorrect) From 9e88ae4e53660eca08b1f54becfa851d8d277163 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sat, 1 Jan 2022 19:25:34 -0500 Subject: [PATCH 079/285] Feat/show graph/probe color (#2268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * - * • test_control.py: - add test_warning_for_add_controller_twice() - add test_warning_for_controller_assigned_to_another_comp() * • test_control.py: - add test_warning_for_replacement_of_controller() * - * - * • composition.py - _check_for_unused_projections(): fix crash for projections in deferred_init - _check_controller_initialization_status(): edited warning message - _add_node_aux_components(): add projections in deferred_init to invalid_aux_components * • test_control.py: - add test_add_node_with_controller_spec_and_control_mech_but_not_a_controller() * • test_control.py: = add test_bad_objective_mechanism_spec() * • test_control.py: = add test_bad_objective_mechanism_spec() * - * • optimizationcontrolmechanism.py: update figure in docstring * • optimizationcontrolmechanism.py: document state_features attribute * • optimizationcontrolmechanism.py: - docs: - document state_feature_attribute - update figure - implement state property - _update_state_input_ports_for_controller(): fix bug in which assignment of OutputPort as state_feature caused crash * • composition.py: - _get_total_cost_of_control_allocation(): add get_controller to find controller for which self (Composition) is the agent_rep * • show_graph.py - show_graph(): add show_all option * - * • show_graph: add probe_color • composition.py: docstring edits re: PROBE * - * • show_graph.py: add handling of probe_color for Projections (in progress) * - * • show_graph: supports probe_color for projections out output_CIM in outer comp if include_probes_in_output for that comp is False * • test_control.py: - add test_objective_mechanism_spec_as_monitor_for_control_error() Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- .../modulatory/control/controlmechanism.py | 22 ++++-- .../control/optimizationcontrolmechanism.py | 76 +++++-------------- .../compositioninterfacemechanism.py | 50 +++++++----- psyneulink/core/compositions/composition.py | 40 ++++++---- psyneulink/core/compositions/showgraph.py | 71 ++++++++++------- tests/composition/test_control.py | 9 ++- 6 files changed, 142 insertions(+), 126 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index 3886991670c..f18b3c95132 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -92,7 +92,7 @@ *Specifying OutputPorts to be monitored* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -A ControlMechanism can be configured to monitor the output of other Mechanisms directly (by receiving direct +A ControlMechanism can be configured to monitor the output of other Mechanisms either directly (by receiving direct Projections from their OutputPorts), or by way of an `ObjectiveMechanism` that evaluates those outputs and passes the result to the ControlMechanism (see `below ` for more detailed description). The following figures show an example of each: @@ -1282,8 +1282,8 @@ def __init__(self, **kwargs ): - monitor_for_control = convert_to_list(monitor_for_control) or [] control = convert_to_list(control) or [] + monitor_for_control = convert_to_list(monitor_for_control) or [] self.allow_probes = allow_probes # For backward compatibility: @@ -1356,9 +1356,17 @@ def _validate_params(self, request_set, target_set=None, context=None): target_set=target_set, context=context) - if OBJECTIVE_MECHANISM in target_set and \ - target_set[OBJECTIVE_MECHANISM] is not None and\ - target_set[OBJECTIVE_MECHANISM] is not False: + if (MONITOR_FOR_CONTROL in target_set + and target_set[MONITOR_FOR_CONTROL] is not None + and any(item for item in target_set[MONITOR_FOR_CONTROL] + if (isinstance(item, ObjectiveMechanism) or item is ObjectiveMechanism))): + raise ControlMechanismError(f"The '{MONITOR_FOR_CONTROL}' arg of '{self.name}' contains a specification for" + f" an {ObjectiveMechanism.componentType} ({target_set[MONITOR_FOR_CONTROL]}). " + f"This should be specified in its '{OBJECTIVE_MECHANISM}' argument.") + + if (OBJECTIVE_MECHANISM in target_set and + target_set[OBJECTIVE_MECHANISM] is not None + and target_set[OBJECTIVE_MECHANISM] is not False): if isinstance(target_set[OBJECTIVE_MECHANISM], list): @@ -1653,7 +1661,9 @@ def _parse_monitor_for_control_input_ports(self, context): return outcome_input_port_specs, port_value_sizes, monitored_ports def _validate_monitor_for_control(self, nodes): - # Ensure all of the Components being monitored for control are in the Composition being controlled + """Ensure all of the Components being monitored for control are in the Composition being controlled + If monitor_for_control is specified as an ObjectiveMechanism, warn and move to objective_mecahnism arg + """ from psyneulink.core.components.ports.port import Port invalid_outcome_specs = [item for item in self.monitor_for_control if ((isinstance(item, Mechanism) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 067ceda8445..5c654b8b5b4 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -577,20 +577,25 @@ then its `outcome_input_ports ` are determined by its `monitor_for_control ` and `outcome_input_ports_option ` attributes, specified in the corresponding arguments of its -constructor (see `Outcomes arguments `), and the `allow_probes -` attribute of the Composition for which the OptimizationControlMechanism is the -`controller `. The latter allows the values of the items listed in `monitor_for_control -` to be `INPUT ` or `INTERNAL ` `Nodes -` of a `nested Composition ` to be monitored and included in the computation -of `outcome ` (ordinarily, those must be `OUTPUT ` Nodes of a nested -Composition). This can be thought of as providing access to "latent variables" of the Composition being evaluated; -that is, ones that do not contribute directly to the Composition's `results `. This -applies both to items that are monitored directly by the OptimizationControlMechanism or via its ObjectiveMechanism -(see `allow_probes ` above for additional details). - -The value(s) of the specified Components are assigned as the OptimizationControlMechanism's `outcome -` attribute, which is used to compute the `net_outcome ` -of executing its `agent_rep `. +constructor (see `Outcomes arguments `). The value(s) of the specified +Components are assigned as the OptimizationControlMechanism's `outcome ` attribute, +which is used to compute the `net_outcome ` of executing its `agent_rep +`. + +COMMENT: + FIX: 1/1/22 + .. note:: + , and the `allow_probes + ` attribute of the Composition for which the OptimizationControlMechanism is the + `controller `. The latter allows the values of the items listed in `monitor_for_control + ` to be `INPUT ` or `INTERNAL ` `Nodes + ` of a `nested Composition ` to be monitored and included in the computation + of `outcome ` (ordinarily, those must be `OUTPUT ` Nodes of a nested + Composition). This can be thought of as providing access to "latent variables" of the Composition being evaluated; + that is, ones that do not contribute directly to the Composition's `results `. This + applies both to items that are monitored directly by the OptimizationControlMechanism or via its ObjectiveMechanism. +COMMENT + .. _OptimizationControlMechanism_Function: @@ -912,7 +917,6 @@ class OptimizationControlMechanism(ControlMechanism): state_features=None, \ state_feature_functions=None, \ monitor_for_control=None, \ - allow_probes=False, \ objective_mechanism=None, \ function=GridSearch, \ num_estimates=1, \ @@ -1555,48 +1559,6 @@ def _validate_monitor_for_control(self, nodes): f"Projections from the following Components that do not " f"belong to its {AGENT_REP} ({self.agent_rep.name}): {e.data}.") - # FIX: 12/9/21 -- DEPRECATE DIRECT PROJECTIONS FROM PROBES, ELIMINATING THE NEED FOR THIS OVERRIDE - # def _parse_monitor_for_control_input_ports(self, context): - # """Override ControlMechanism to implement allow_probes=DIRECT option - # - # If is False (default), simply pass results of super()._parse_monitor_for_control_input_ports(context); - # this is restricted to the use of OUTPUT Nodes in nested Compositions, and routes Projections from nodes in - # nested Compositions through their respective output_CIMs. - # - # If allow_probes option is True, any INTERNAL Nodes of nested Compositions specified in monitor_for_control - # are assigned NodeRole.OUTPUT, and Projections from them to the OptimizationControlMechanism are routed - # from the nested Composition(s) through the respective output_CIM(s). - # - # If allow_probes option is DIRECT, Projection specifications are added to Port specification dictionaries, - # so that the call to super()._instantiate_input_ports in ControlMechanism instantiates Projections from - # monitored node to OptimizationControlMechanism. This allows *direct* Projections from monitored nodes in - # nested Compositions to the OptimizationControlMechanism, bypassing output_CIMs and preventing inclusion - # of their values in the results attribute of those Compositions. - # - # Return port specification dictionaries (*with* Projection specifications), their value sizes and null list - # (to suppress Projection assignment to aux_components in ControlMechanism._instantiate_input_ports) - # """ - # - # outcome_input_port_specs, outcome_value_sizes, monitored_ports \ - # = super()._parse_monitor_for_control_input_ports(context) - # - # if self.allow_probes == DIRECT: - # # Add Projection specifications to port specification dictionaries for outcome_input_ports - # # and return monitored_ports = [] - # - # if self.outcome_input_ports_option == SEPARATE: - # # Add port spec to to each outcome_input_port_spec (so that a Projection is specified directly to each) - # for i in range(self.num_outcome_input_ports): - # outcome_input_port_specs[i].update({PROJECTIONS: monitored_ports[i]}) - # else: - # # Add all ports specs as list to single outcome_input_port - # outcome_input_port_specs[0].update({PROJECTIONS: monitored_ports}) - # - # # Return [] for ports to suppress creation of Projections in _instantiate_input_ports - # monitored_ports = [] - # - # return outcome_input_port_specs, outcome_value_sizes, monitored_ports - def _update_state_input_ports_for_controller(self, context=None): """Check and update state_input_ports for model-based optimization (agent_rep==Composition) diff --git a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py index 53e9469eedd..0c378bfced0 100644 --- a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py +++ b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py @@ -232,40 +232,54 @@ def remove_ports(self, ports, context=None): output_ports_marked_for_deletion.add(port) self.user_added_ports[OUTPUT_PORTS] = self.user_added_ports[OUTPUT_PORTS] - output_ports_marked_for_deletion - def _get_destination_node_for_input_port(self, input_port, comp=None): - """Return Port, Node and Composition for destination of projection from input_CIM to (possibly nested) node""" + def _get_destination_node_for_input_CIM(self, port, comp=None): + """Return Port, Node and Composition for destination of projection from input_CIM to (possibly nested) node + **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; + **comp** specifies the Composition at which to begin the search; assumes the current + CompositionInterfaceMechanism's Composition by default + """ + # Ensure method is being called on an input_CIM + assert self == self.composition.input_CIM # CIM MAP ENTRIES: [RECEIVER PORT, [input_CIM InputPort, input_CIM OutputPort]] - # Get sender to input_port of CIM for corresponding output_port + # Get receiver of output_port of input_CIM comp = comp or self - port_map = input_port.owner.port_map - output_port = [port_map[k][1] for k in port_map if port_map[k][0] is input_port] - assert len(output_port)==1, f"PROGRAM ERROR: Expected only 1 output_port for {input_port.name} " \ - f"in port_map for {input_port.owner}; found {len(output_port)}." + port_map = port.owner.port_map + idx = 0 if isinstance(port, InputPort) else 1 + output_port = [port_map[k][1] for k in port_map if port_map[k][idx] is port] + assert len(output_port)==1, f"PROGRAM ERROR: Expected exactly 1 output_port for {port.name} " \ + f"in port_map for {port.owner}; found {len(output_port)}." assert len(output_port[0].efferents)==1, f"PROGRAM ERROR: Port ({output_port.name}) expected to have " \ f"just one efferent; has {len(output_port.efferents)}." receiver = output_port[0].efferents[0].receiver if not isinstance(receiver.owner, CompositionInterfaceMechanism): return receiver, receiver.owner, comp - return self._get_destination_node_for_input_port(receiver, receiver.owner.composition) + return self._get_destination_node_for_input_CIM(receiver, receiver.owner.composition) - def _get_source_node_for_output_port(self, output_port, comp=None): - """Return Port, Node and Composition for source of projection to output_CIM from (possibly nested) node""" + def _get_source_node_for_output_CIM(self, port, comp=None): + """Return Port, Node and Composition for source of projection to output_CIM from (possibly nested) node + **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; + **comp** specifies the Composition at which to begin the search; assumes the current + CompositionInterfaceMechanism's Composition by default + """ + # Ensure method is being called on an output_CIM + assert self == self.composition.output_CIM # CIM MAP ENTRIES: [SENDER PORT, [output_CIM InputPort, output_CIM OutputPort]] - # Get sender to input_port of CIM for corresponding output_port - comp = comp or self - port_map = output_port.owner.port_map - input_port = [port_map[k][0] for k in port_map if port_map[k][1] is output_port] - assert len(input_port)==1, f"PROGRAM ERROR: Expected only 1 input_port for {output_port.name} " \ - f"in port_map for {output_port.owner}; found {len(input_port)}." + # Get sender to input_port of output_CIM + comp = comp or self.composition + port_map = port.owner.port_map + idx = 0 if isinstance(port, InputPort) else 1 + input_port = [port_map[k][0] for k in port_map if port_map[k][idx] is port] + assert len(input_port)==1, f"PROGRAM ERROR: Expected exactly 1 input_port for {port.name} " \ + f"in port_map for {port.owner}; found {len(input_port)}." assert len(input_port[0].path_afferents)==1, f"PROGRAM ERROR: Port ({input_port.name}) expected to have " \ f"just one path_afferent; has {len(input_port.path_afferents)}." sender = input_port[0].path_afferents[0].sender if not isinstance(sender.owner, CompositionInterfaceMechanism): return sender, sender.owner, comp - return self._get_source_node_for_output_port(sender, sender.owner.composition) + return self._get_source_node_for_output_CIM(sender, sender.owner.composition) def _sender_is_probe(self, output_port): """Return True if source of output_port is a PROBE Node of the Composition to which it belongs""" from psyneulink.core.compositions.composition import NodeRole - port, node, comp = self._get_source_node_for_output_port(output_port, self.composition) + port, node, comp = self._get_source_node_for_output_CIM(output_port, self.composition) return NodeRole.PROBE in comp.get_roles_by_node(node) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index ce064414ff3..4a202c0828d 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -339,21 +339,31 @@ .. _Composition_Probes: -* *Probes* -- Nodes that are not `OUTPUT ` of a nested Composition but project to ones in an - outer Composition are assigned `PROBE ` in addition to their other `roles ` in the +* *Probes* -- Nodes that are not `OUTPUT ` of a nested Composition, but project to ones in an + outer Composition, are assigned `PROBE ` in addition to their other `roles ` in the nested Composition. The only difference between `PROBE ` and `OUTPUT ` Nodes is whether their output is included in the `output_values ` and `results - ` attributes of the outermost Composition to which they project; this is determined by the + ` attributes of the *outermost* Composition to which they project; this is determined by the `include_probes_in_output ` attribute of the latter. If `include_probes_in_output ` is False (the default), then the output of any - `PROBE ` Nodes in any Composition nested within it are *not* included in - the `output_values ` or `results ` for the Composition to which - they project. In this respect, they can be thought of as "probing" - that is, providing access to "latent variables" - of -- the Composition to which they belong -- the values of which that are not otherwise reported as part of the - Composition's output or results. If `include_probes_in_output ` is True, - then any `PROBE ` Nodes of any nested Compositions are treated the same as `OUTPUT ` - Nodes: their outputs are included in the `output_values ` and `results - ` of that Composition. + `PROBE ` Nodes are *not* included in the `output_values ` or `results + ` for the outermost Composition to which they project (although they *are* still included + in those attributes of the nested Compositions; see note below). In this respect, they can be thought of as + "probing" - that is, providing access to "latent variables" of -- the nested Composition to which they belong -- + the values of which that are not otherwise reported as part of the outermost Composition's output or results. If + `include_probes_in_output ` is True, then any `PROBE ` Nodes + of any nested Compositions are treated the same as `OUTPUT ` Nodes: their outputs are included in + the `output_values ` and `results ` of the outermost Composition. + `PROBE ` Nodes can be visualized, along with any Projections treated differently from those of + `OUTPUT ` Nodes (i.e., when `include_probes_in_output ` is + False), using the Composition's `show_graph ` method, which displays them in their own color + (pink by default). + + .. hint:: + `PROBE ` Nodes are useful for `model-based optimization using an + `, in which the value of one or more Nodes in a nested Composition + may need to be `monitored ` without being considered as + part of the output or results of the Composition being optimized. .. note:: The specification of `include_probes_in_output ` only applies to a @@ -379,7 +389,7 @@ which it is nested, including the outermost one, then when the latter is `executed `, both the `output_values ` and `results ` of the nested Composition are also included in those attributes of any intervening and the outermost Composition. If `allow_probes -` is set, then the Composition's `include_probes_in_output +` is set (which it is by default), then the Composition's `include_probes_in_output ` attribute determines whether their values are also included in the `output_values ` and `results ` of the outermost Composition (see `above `). @@ -8581,7 +8591,7 @@ def _parse_labels(self, inputs, mech=None, context=None): and any(n.input_labels_dict for n in k._get_nested_nodes_with_same_roles_at_all_levels(k,NodeRole.INPUT))): for i, port in enumerate(k.input_CIM.input_ports): - _, mech_with_labels, __ = k.input_CIM._get_destination_node_for_input_port(port) + _, mech_with_labels, __ = k.input_CIM._get_destination_node_for_input_CIM(port) v[i] = k._parse_labels(inputs[k][i],mech_with_labels) _inputs.update({k:v}) else: @@ -10434,7 +10444,7 @@ def _get_inputs(comp, nesting_level=1, use_labels=False): in node._get_nested_nodes_with_same_roles_at_all_levels(node, NodeRole.INPUT))): input_values = [] for i, port in enumerate(node.input_CIM.input_ports): - _, mech, __ = node.input_CIM._get_destination_node_for_input_port(port) + _, mech, __ = node.input_CIM._get_destination_node_for_input_CIM(port) labels_dict = mech.input_labels_dict if labels_dict: labels = list(labels_dict[0].keys()) @@ -10523,7 +10533,7 @@ def get_results_by_nodes(self, warnings.warn(f"{alias} is aliased to get_results_by_nodes(); please use that in the future.") # Get all OUTPUT Nodes in (nested) Composition(s) - output_nodes = [self.output_CIM._get_source_node_for_output_port(port)[1] + output_nodes = [self.output_CIM._get_source_node_for_output_CIM(port)[1] for port in self.output_CIM.output_ports] # Get all values for all OUTPUT Nodes diff --git a/psyneulink/core/compositions/showgraph.py b/psyneulink/core/compositions/showgraph.py index 26bb36fc010..ed34d3773f8 100644 --- a/psyneulink/core/compositions/showgraph.py +++ b/psyneulink/core/compositions/showgraph.py @@ -338,6 +338,9 @@ class ShowGraph(): input_color : keyword : default 'green', specifies the color in which `INPUT ` Nodes of the Composition are displayed. + probe_color : keyword : default 'pink', + specifies the color in which `PROBE ` Nodes of the Composition are displayed. + output_color : keyword : default 'red', specifies the color in which `OUTPUT ` Nodes of the Composition are displayed. @@ -410,6 +413,7 @@ def __init__(self, default_node_color = 'black', active_color=BOLD, input_color='green', + probe_color='pink', output_color='red', input_and_output_color='brown', # feedback_color='yellow', @@ -448,6 +452,7 @@ def __init__(self, self.default_node_color = default_node_color self.active_color = active_color self.input_color = input_color + self.probe_color=probe_color self.output_color = output_color self.input_and_output_color = input_and_output_color # self.feedback_color = self.feedback_color @@ -930,6 +935,8 @@ def _assign_processing_components(self, nested_comp_graph.attr(color=self.input_and_output_color) elif rcvr in composition.get_nodes_by_role(NodeRole.INPUT): nested_comp_graph.attr(color=self.input_color) + elif rcvr in composition.get_nodes_by_role(NodeRole.PROBE): + nested_comp_graph.attr(color=self.probe_color) elif rcvr in composition.get_nodes_by_role(NodeRole.OUTPUT): nested_comp_graph.attr(color=self.output_color) nested_comp_graph.attr(label=rcvr_label) @@ -1009,6 +1016,20 @@ def _assign_processing_components(self, rcvr_penwidth = str(self.bold_width) rcvr_rank = self.input_rank + # PROBE Node + elif rcvr in composition.get_nodes_by_role(NodeRole.PROBE): + if rcvr in active_items: + if self.active_color == BOLD: + rcvr_color = self.probe_color + else: + rcvr_color = self.active_color + rcvr_penwidth = str(self.bold_width + self.active_thicker_by) + composition.active_item_rendered = True + else: + rcvr_color = self.probe_color + rcvr_penwidth = str(self.bold_width) + rcvr_rank = self.output_rank + # OUTPUT Node elif rcvr in composition.get_nodes_by_role(NodeRole.OUTPUT): if rcvr in active_items: @@ -1471,6 +1492,11 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, continue else: proj_color=self.inactive_projection_color + else: + port, node, comp = cim._get_source_node_for_output_CIM(proj.receiver) + if (node in comp.get_nodes_by_role(NodeRole.PROBE) + and not composition.include_probes_in_output): + proj_color=self.probe_color sndr_output_node_proj = proj.sender if (isinstance(sndr_output_node_proj.owner, CompositionInterfaceMechanism) @@ -1478,8 +1504,7 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, sndr_output_node_proj_owner = sndr_output_node_proj.owner.composition else: sndr_output_node_proj_owner = sndr_output_node_proj.owner - # Validate the Projection is from an OUTPUT node - # or a PROBE node if allow_probes is set for a controller or its objective_mechanism + # Validate the Projection is from an OUTPUT or PROBE node if ((sndr_output_node_proj_owner in composition.nodes_to_roles and not any(role for role in {NodeRole.OUTPUT, NodeRole.PROBE} if role in composition.nodes_to_roles[sndr_output_node_proj_owner]))): @@ -1511,7 +1536,7 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, sndr_output_node_proj_label = sndr_label rcvr_output_cim_proj_label = cim_label - # FIX 6/23/20 PROBLEM POINT: + # FIX 6/23/20 PROBLEM POINT: (SEE MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c) # Render Projection _render_projection(g, proj, @@ -1530,7 +1555,6 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, continue else: proj_color=self.inactive_projection_color - rcvr_node_input_port = proj.receiver # Skip if receiver is controller of enclosing_comp (handled by _assign_controller_components) @@ -1615,10 +1639,10 @@ def _assign_controller_components(self, f"so \'show_controller\' option in call to its show_graph() method will be ignored.") return + # Assign colors, penwidth and label displayed for controller and ControlProjections --------------------- + ctlr_color = self.controller_color if controller in active_items: - if self.active_color == BOLD: - ctlr_color = self.controller_color - else: + if self.active_color != BOLD: ctlr_color = self.active_color ctlr_width = str(self.default_width + self.active_thicker_by) composition.active_item_rendered = True @@ -1626,6 +1650,15 @@ def _assign_controller_components(self, ctlr_color = self.controller_color ctlr_width = str(self.default_width) + ctl_proj_color = self.controller_color + if controller in active_items: + if self.active_color != BOLD: + ctl_proj_color = self.active_color + ctl_proj_width = str(self.default_width + self.active_thicker_by) + composition.active_item_rendered = True + else: + ctl_proj_width = str(self.default_width) + # Assign controller node node_shape = self.mechanism_shape ctlr_label = self._get_graph_node_label(composition, controller, show_types, show_dimensions) @@ -1725,17 +1758,6 @@ def find_rcvr_comp(r, c, l): ctl_proj_sndr_label = ctlr_label ctl_proj_rcvr_label = rcvr_label - # Assign colors, penwidth and label displayed for ControlProjection --------------------- - if controller in active_items: - if self.active_color == BOLD: - ctl_proj_color = self.controller_color - else: - ctl_proj_color = self.active_color - ctl_proj_width = str(self.default_width + self.active_thicker_by) - composition.active_item_rendered = True - else: - ctl_proj_color = self.controller_color - ctl_proj_width = str(self.default_width) if show_projection_labels: edge_label = ctl_proj.name else: @@ -1869,16 +1891,6 @@ def find_rcvr_comp(r, c, l): # incoming edges (from monitored mechs directly to controller) for outcome_input_port in controller.outcome_input_ports: for projection in outcome_input_port.path_afferents: - if controller in active_items: - if self.active_color == BOLD: - proj_color = self.controller_color - else: - proj_color = self.active_color - proj_width = str(self.default_width + self.active_thicker_by) - composition.active_item_rendered = True - else: - proj_color = self.controller_color - proj_width = str(self.default_width) if show_node_structure: sndr_proj_label = self._get_graph_node_label(composition, projection.sender.owner, @@ -1912,7 +1924,8 @@ def find_rcvr_comp(r, c, l): else: edge_label = '' g.edge(sndr_proj_label, ctlr_input_proj_label, label=edge_label, - color=proj_color, penwidth=proj_width) + # color=proj_color, penwidth=proj_width) + color=ctl_proj_color, penwidth=ctl_proj_width) # If controller has an agent_rep, assign its node and edges (not Projections per se) if hasattr(controller, 'agent_rep') and controller.agent_rep and show_controller==AGENT_REP : diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 3fb47187d4d..3df12dd9dd0 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -127,7 +127,14 @@ def test_bad_objective_mechanism_spec(self): '(ProcessingMechanism-0) must be an ObjectiveMechanism or a list of Mechanisms ' \ 'and/or OutputPorts to be monitored for control.' with pytest.raises(pnl.ControlMechanismError) as error: - comp = pnl.Composition(controller=pnl.ControlMechanism(objective_mechanism=mech)) + pnl.Composition(controller=pnl.ControlMechanism(objective_mechanism=mech)) + error_msg = error.value.error_value + assert expected_error in error_msg + + def test_objective_mechanism_spec_as_monitor_for_control_error(self): + expected_error = 'The \'monitor_for_control\' arg of \'ControlMechanism-0\' contains a specification for an ObjectiveMechanism ([(ObjectiveMechanism ObjectiveMechanism-0)]). This should be specified in its \'objective_mechanism\' argument.' + with pytest.raises(pnl.ControlMechanismError) as error: + pnl.Composition(controller=pnl.ControlMechanism(monitor_for_control=pnl.ObjectiveMechanism())) error_msg = error.value.error_value assert expected_error in error_msg From 1fdb95da128ed07b656b79abdd8e55e845b1f29b Mon Sep 17 00:00:00 2001 From: jdcpni Date: Mon, 3 Jan 2022 11:09:16 -0500 Subject: [PATCH 080/285] Feat/control signal/add control arg (#2272) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * • controlmechanism.py: allow CONTROL as alias for PROJECTIONS in ControlSignal spec * • controlsignal.py: allow CONTROL as alias for PROJECTIONS in ControlSignal spec * • controlsignal.py: replaces modulates with control as arg (though still allow modulates) * • test_projection_specifications.py: - add test_control_signal_projections_arg * - * • gatingsignal.py: - __init__: replace modulates arg with gate * - * - * - * - * • CONTROL and GATE in place of PROJECTIONS Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- .../modulatory/control/controlmechanism.py | 34 +++-- .../control/gating/gatingmechanism.py | 66 ++++++++- .../control/optimizationcontrolmechanism.py | 30 ++-- .../ports/modulatorysignals/controlsignal.py | 93 +++++++++---- .../ports/modulatorysignals/gatingsignal.py | 53 +++++++- .../modulatorysignals/modulatorysignal.py | 2 +- psyneulink/core/components/ports/port.py | 19 ++- psyneulink/core/globals/parameters.py | 13 +- tests/composition/test_composition.py | 11 +- tests/composition/test_control.py | 36 ++--- tests/mechanisms/test_control_mechanism.py | 5 +- tests/naming/test_naming.py | 7 +- .../test_projection_specifications.py | 128 +++++++++++++++--- 13 files changed, 375 insertions(+), 122 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index f18b3c95132..27743dabdea 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1210,6 +1210,9 @@ class Parameters(ModulatoryMechanism_Base.Parameters): constructor_argument='control' ) + # MODIFIED 1/2/22 OLD: - MUCH OF THIS SEEMS TO BE COVERED ELSEWHERE; COMMENTING OUT ONLY CAUSES PROBLEMS WITH + # test_control_signal_and_control_projection_names AND + # test_json_results_equivalence (stroop_conflict_monitoring_py) def _parse_output_ports(self, output_ports): def is_2tuple(o): return isinstance(o, tuple) and len(o) == 2 @@ -1231,19 +1234,28 @@ def is_2tuple(o): MECHANISM: output_ports[i][1] } # handle dict of form {PROJECTIONS: <2 item tuple>, : , ...} - elif ( - isinstance(output_ports[i], dict) - and PROJECTIONS in output_ports[i] - and is_2tuple(output_ports[i][PROJECTIONS]) - ): - full_spec_dict = { - NAME: output_ports[i][PROJECTIONS][0], - MECHANISM: output_ports[i][PROJECTIONS][1], - **{k: v for k, v in output_ports[i].items() if k != PROJECTIONS} - } - output_ports[i] = full_spec_dict + elif isinstance(output_ports[i], dict): + # Handle CONTROL as synonym of PROJECTIONS + if CONTROL in output_ports[i]: + # MODIFIED 1/3/22 NEW: + # CONTROL AND PROJECTIONS can't both be used + if PROJECTIONS in output_ports[i]: + raise ControlMechanismError(f"Both 'CONTROL' and 'PROJECTIONS' entries found in " + f"specification dict for {ControlSignal.__name__} of " + f"'{self.name}': ({output_ports[i]}).") + # MODIFIED 1/3/22 END + # Replace CONTROL with PROJECTIONS + output_ports[i][PROJECTIONS] = output_ports[i].pop(CONTROL) + if (PROJECTIONS in output_ports[i] and is_2tuple(output_ports[i][PROJECTIONS])): + full_spec_dict = { + NAME: output_ports[i][PROJECTIONS][0], + MECHANISM: output_ports[i][PROJECTIONS][1], + **{k: v for k, v in output_ports[i].items() if k != PROJECTIONS} + } + output_ports[i] = full_spec_dict return output_ports + # MODIFIED 1/2/22 END def _validate_input_ports(self, input_ports): if input_ports is None: diff --git a/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py index dbf03790b30..ed919e5bb67 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py @@ -187,7 +187,7 @@ from psyneulink.core.components.ports.modulatorysignals.gatingsignal import GatingSignal from psyneulink.core.globals.defaults import defaultGatingAllocation from psyneulink.core.globals.keywords import \ - GATING, GATING_PROJECTION, GATING_SIGNAL, GATING_SIGNALS, \ + GATE, GATING, GATING_PROJECTION, GATING_SIGNAL, GATING_SIGNALS, \ INIT_EXECUTE_METHOD_ONLY, MONITOR_FOR_CONTROL, PROJECTION_TYPE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set @@ -399,6 +399,13 @@ class Parameters(ControlMechanism.Parameters): :default value: numpy.array([0.5]) :type: ``numpy.ndarray`` + output_ports + see `output_ports ` + + :default value: None + :type: + :read only: True + gating_allocation see `gating_allocation ` @@ -413,6 +420,63 @@ class Parameters(ControlMechanism.Parameters): pnl_internal=True ) + output_ports = Parameter( + None, + stateful=False, + loggable=False, + read_only=True, + structural=True, + parse_spec=True, + aliases=['control', 'control_signals', 'gate', 'gating_signal'], + constructor_argument='gate' + ) + + def _parse_output_ports(self, output_ports): + from psyneulink.core.globals.keywords import NAME, MECHANISM, PROJECTIONS + # # FIX: 1/2/22 - ANY WAY TO CALL SUPER TO CALL ControlMechanism.parameters.output_ports._parse_output_ports + # super().output_ports._parse_output_ports(output_ports) + + def is_2tuple(o): + return isinstance(o, tuple) and len(o) == 2 + + if not isinstance(output_ports, list): + output_ports = [output_ports] + + for i in range(len(output_ports)): + # handle 2-item tuple + if is_2tuple(output_ports[i]): + + # this is an odd case that uses two names in the name entry + # unsure what it means + if isinstance(output_ports[i][0], list): + continue + + output_ports[i] = { + NAME: output_ports[i][0], + MECHANISM: output_ports[i][1] + } + # handle dict of form {PROJECTIONS: <2 item tuple>, : , ...} + elif isinstance(output_ports[i], dict): + # Handle GATE as synonym of PROJECTIONS + if GATE in output_ports[i]: + # GATE AND PROJECTIONS can't both be used + if PROJECTIONS in output_ports[i]: + raise GatingMechanismError(f"Both 'PROJECTIONS' and 'GATE' entries found in " + f"specification dict for {GatingSignal.__name__} of " + f"'{self.name}': ({output_ports[i]}).") + # Replace GATE with PROJECTIONS + output_ports[i][PROJECTIONS] = output_ports[i].pop(GATE) + if (PROJECTIONS in output_ports[i] and is_2tuple(output_ports[i][PROJECTIONS])): + full_spec_dict = { + NAME: output_ports[i][PROJECTIONS][0], + MECHANISM: output_ports[i][PROJECTIONS][1], + **{k: v for k, v in output_ports[i].items() if k != PROJECTIONS} + } + output_ports[i] = full_spec_dict + + return output_ports + + @tc.typecheck def __init__(self, default_gating_allocation=None, diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 5c654b8b5b4..59a0b533cbc 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -582,20 +582,16 @@ which is used to compute the `net_outcome ` of executing its `agent_rep `. -COMMENT: - FIX: 1/1/22 .. note:: - , and the `allow_probes - ` attribute of the Composition for which the OptimizationControlMechanism is the - `controller `. The latter allows the values of the items listed in `monitor_for_control - ` to be `INPUT ` or `INTERNAL ` `Nodes - ` of a `nested Composition ` to be monitored and included in the computation - of `outcome ` (ordinarily, those must be `OUTPUT ` Nodes of a nested - Composition). This can be thought of as providing access to "latent variables" of the Composition being evaluated; - that is, ones that do not contribute directly to the Composition's `results `. This - applies both to items that are monitored directly by the OptimizationControlMechanism or via its ObjectiveMechanism. -COMMENT - + If a `Node ` other than an `OUTPUT ` of a `nested ` + Composition is `specified to be monitored `, it is assigned as a `PROBE + ` of that nested Composition. Although `PROBE ` Nodes are generally treated + like `OUTPUT ` Nodes (since they project out of the Composition to which they belong), their + `value ` is not included in the `output_values ` or `results + ` attributes of the Composition for which the OptimizationControlMechanism is the + `controller `, unless that Composition's `include_probes_in_output + ` attribute is set to True (see `Composition_Probes` for additional + information). .. _OptimizationControlMechanism_Function: @@ -2322,14 +2318,6 @@ def _gen_llvm_output_port_parse_variable(self, ctx, builder, params, context, va builder.store(builder.load(val_ptr), dest_ptr) return oport_input - # Deprecated - this is now a Parameter - # @property - # def state_feature_values(self): - # if hasattr(self.agent_rep, 'model_based_optimizer') and self.agent_rep.model_based_optimizer is self: - # return self.agent_rep._get_predicted_input() - # else: - # return np.array(np.array(self.variable[1:]).tolist()) - @property def agent_rep_type(self): from psyneulink.core.compositions.compositionfunctionapproximator import CompositionFunctionApproximator diff --git a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py index 5bc8b48516a..a2ed0953f72 100644 --- a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py @@ -64,7 +64,7 @@ `, the parameter(s) to be controlled must be specified. If other attributes of the ControlSignal need to be specified (e.g., one or more of its `cost functions `), then the Constructor for the ControlSignal can be used or a `port specification dictionary `, in which the parameter(s) to be -controlled in the **projections** argument or *PROJECTIONS* entry, respectively, using any of the forms below. +controlled are specified in the **control** argument or *CONTROL* entry, respectively, using any of the forms below. For convenience, the parameters can also be specified on their own in the **control_signals** argument of the ControlMechanism's constructor, in which case a default ControlSignal will be created for each. In all cases, any of the following can be use to specify the parameter(s) to be controlled: @@ -114,10 +114,10 @@ ~~~~~~~~~~~~~ When a ControlSignal is created, it can be assigned one or more `ControlProjections `, using either -the **projections** argument of its constructor, or in an entry of a dictionary assigned to the **params** argument -with the key *PROJECTIONS*. These will be assigned to its `efferents ` attribute. See +the **control** argument of its constructor, or in an entry of a dictionary assigned to the **params** argument +with the key *CONTROL*. These are assigned to its `efferents ` attribute. See `Port Projections ` for additional details concerning the specification of Projections when -creating a Port. +creating a Port, including `examples ` of ControlProjection specification. .. note:: Although a ControlSignal can be assigned more than one `ControlProjection`, all of those Projections will receive @@ -347,7 +347,7 @@ >>> from psyneulink import * >>> mech = ProcessingMechanism(name='my_mech') >>> ctl_mech_A = ControlMechanism(monitor_for_control=mech, - ... control_signals=ControlSignal(modulates=(INTERCEPT,mech), + ... control_signals=ControlSignal(control=(INTERCEPT,mech), ... cost_options=CostFunctions.INTENSITY)) >>> ctl_mech_B = ControlMechanism(monitor_for_control=mech, ... control_signals=ControlSignal(modulates=ctl_mech_A.control_signals[0], @@ -398,26 +398,28 @@ """ +import warnings + import numpy as np import typecheck as tc -import warnings # FIX: EVCControlMechanism IS IMPORTED HERE TO DEAL WITH COST FUNCTIONS THAT ARE DEFINED IN EVCControlMechanism # SHOULD THEY BE LIMITED TO EVC?? from psyneulink.core import llvm as pnlvm -from psyneulink.core.components.functions.nonstateful.combinationfunctions import Reduce from psyneulink.core.components.functions.function import is_function_type +from psyneulink.core.components.functions.nonstateful.combinationfunctions import Reduce +from psyneulink.core.components.functions.nonstateful.transferfunctions import Exponential, Linear, CostFunctions, \ + TransferWithCosts from psyneulink.core.components.functions.stateful.integratorfunctions import SimpleIntegrator -from psyneulink.core.components.functions.nonstateful.transferfunctions import Exponential, Linear, CostFunctions, TransferWithCosts from psyneulink.core.components.ports.modulatorysignals.modulatorysignal import ModulatorySignal from psyneulink.core.components.ports.outputport import _output_port_variable_getter from psyneulink.core.globals.context import ContextFlags from psyneulink.core.globals.defaults import defaultControlAllocation from psyneulink.core.globals.keywords import \ - ALLOCATION_SAMPLES, CONTROL_PROJECTION, CONTROL_SIGNAL, \ - INPUT_PORT, INPUT_PORTS, \ + ALLOCATION_SAMPLES, CONTROL, CONTROL_PROJECTION, CONTROL_SIGNAL, \ + INPUT_PORT, INPUT_PORTS, MODULATES, \ OUTPUT_PORT, OUTPUT_PORTS, OUTPUT_PORT_PARAMS, \ - PARAMETER_PORT, PARAMETER_PORTS, \ + PARAMETER_PORT, PARAMETER_PORTS, PROJECTIONS, \ RECEIVER, FUNCTION from psyneulink.core.globals.parameters import FunctionParameter, Parameter, get_validator_by_function from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set @@ -457,7 +459,7 @@ class ControlSignal(ModulatorySignal): duration_cost_function=IntegratorFunction, \ combine_costs_function=Reduce(operation=SUM), \ allocation_samples=self.class_defaults.allocation_samples, \ - modulates=None, \ + control=None, \ projections=None) A subclass of `ModulatorySignal ` used by a `ControlMechanism ` to @@ -499,7 +501,7 @@ class ControlSignal(ModulatorySignal): specifies the values used by the ControlSignal's `owner ` to determine its `control_allocation ` (see `ControlSignal_Execution`). - modulates : list of Projection specifications + control : list of Projection specifications specifies the `ControlProjection(s) ` to be assigned to the ControlSignal, and that will be listed in its `efferents ` attribute (see `ControlSignal_Projections` for additional details). @@ -767,11 +769,11 @@ def _validate_allocation_samples(self, allocation_samples): pass portAttributes = ModulatorySignal.portAttributes | {ALLOCATION_SAMPLES, - COST_OPTIONS, - INTENSITY_COST_FUNCTION, - ADJUSTMENT_COST_FUNCTION, - DURATION_COST_FUNCTION, - COMBINE_COSTS_FUNCTION} + COST_OPTIONS, + INTENSITY_COST_FUNCTION, + ADJUSTMENT_COST_FUNCTION, + DURATION_COST_FUNCTION, + COMBINE_COSTS_FUNCTION} connectsWith = [PARAMETER_PORT, INPUT_PORT, OUTPUT_PORT] connectsWithAttribute = [PARAMETER_PORTS, INPUT_PORTS, OUTPUT_PORTS] @@ -802,7 +804,7 @@ def __init__(self, combine_costs_function:tc.optional(is_function_type)=None, allocation_samples=None, modulation:tc.optional(str)=None, - modulates=None, + control=None, params=None, name=None, prefs:is_pref_set=None, @@ -810,14 +812,36 @@ def __init__(self, try: if kwargs[FUNCTION] is not None: - raise TypeError( - f'{self.__class__.__name__} automatically creates a ' - 'TransferWithCosts function, and does not accept override. ' - 'TransferWithCosts uses the transfer_function parameter.' - ) + raise TypeError(f'{self.__class__.__name__} automatically creates a ' + 'TransferWithCosts function, and does not accept override. ' + 'TransferWithCosts uses the transfer_function parameter.') except KeyError: pass + # Deal with **modulates** if specified + if MODULATES in kwargs: + # Don't allow **control** and **modulates** to both be specified + if control: + raise ControlSignalError(f"Both 'control' and '{MODULATES}' arguments are specified in the " + f"constructor for '{name if name else self.__class__.__name__}; " + f"Should use just 'control'.") + # warnings.warn(f"The '{MODULATES}' argument (specified in the constructor for " + # f"'{name if name else self.__class__.__name__}') has been deprecated; " + # f"should use '{'control'}' going forward.") + + if PROJECTIONS in kwargs: + raise ControlSignalError(f"Both '{MODULATES}' and '{PROJECTIONS}' arguments are specified " + f"in the constructor for '{name if name else self.__class__.__name__}; " + f"Should use just '{PROJECTIONS}' (or 'control') ") + control = kwargs.pop(MODULATES) + + elif PROJECTIONS in kwargs: + # Don't allow **control** and **modulates** to both be specified + if control: + raise ControlSignalError(f"Both 'control' and '{PROJECTIONS}' arguments are specified " + f"in the constructor for '{name if name else self.__class__.__name__}; " + f"Must use just one or the other.") + # This is included in case ControlSignal was created by another Component (such as ControlProjection) # that specified ALLOCATION_SAMPLES in params if params and ALLOCATION_SAMPLES in params and params[ALLOCATION_SAMPLES] is not None: @@ -836,7 +860,7 @@ def __init__(self, size=size, transfer_function=transfer_function, modulation=modulation, - modulates=modulates, + modulates=control, cost_options=cost_options, intensity_cost_function=intensity_cost_function, adjustment_cost_function=adjustment_cost_function, @@ -1012,17 +1036,34 @@ def _parse_port_specific_specs(self, owner, port_dict, port_specific_spec): """ from psyneulink.core.components.projections.projection import _parse_connection_specs - from psyneulink.core.globals.keywords import PROJECTIONS params_dict = {} port_spec = port_specific_spec if isinstance(port_specific_spec, dict): + # MODIFIED 1/2/22 NEW: + # Note: if CONTROL is specified alone, it is moved to PROJECTIONS in Port._parse_ort_spec() + if CONTROL in port_specific_spec and PROJECTIONS in port_specific_spec: + raise ControlSignalError(f"Both 'PROJECTIONS' and 'CONTROL' entries found in specification dict " + f"for '{port_dict['port_type'].__name__}' of '{owner.name}'. " + f"Must use only one or the other.") + # MODIFIED 1/2/22 END return None, port_specific_spec elif isinstance(port_specific_spec, tuple): port_spec = None + # MODIFIED 1/2/22 NEW: + # Resolve CONTROL as synonym for PROJECTIONS: + if CONTROL in params_dict: + # CONTROL AND PROJECTIONS can't both be used + if PROJECTIONS in params_dict: + raise ControlSignalError(f"Both 'PROJECTIONS' and 'CONTROL' entries found in specification dict " + f"for '{port_dict['port_type'].__name__}' of '{owner.name}'. " + f"Must use only one or the other.") + # Move CONTROL to PROJECTIONS + params_dict[PROJECTIONS] = params_dict.pop(CONTROL) + # MODIFIED 1/2/22 END params_dict[PROJECTIONS] = _parse_connection_specs(connectee_port_type=self, owner=owner, connections=port_specific_spec) diff --git a/psyneulink/core/components/ports/modulatorysignals/gatingsignal.py b/psyneulink/core/components/ports/modulatorysignals/gatingsignal.py index 8cba981e069..ee47bb3bf11 100644 --- a/psyneulink/core/components/ports/modulatorysignals/gatingsignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/gatingsignal.py @@ -251,7 +251,7 @@ from psyneulink.core.globals.defaults import defaultGatingAllocation from psyneulink.core.globals.keywords import \ GATE, GATING_PROJECTION, GATING_SIGNAL, INPUT_PORT, INPUT_PORTS, \ - OUTPUT_PORT, OUTPUT_PORTS, OUTPUT_PORT_PARAMS, PROJECTIONS, RECEIVER + MODULATES, OUTPUT_PORT, OUTPUT_PORTS, OUTPUT_PORT_PARAMS, PROJECTIONS, RECEIVER from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel @@ -297,6 +297,11 @@ class GatingSignal(ControlSignal): default_allocation : scalar, list or np.ndarray : defaultGatingAllocation specifies the template and default value used for `allocation `. + gate : list of Projection specifications + specifies the `GatingProjection(s) ` to be assigned to the GatingSignal, and that will be + listed in its `efferents ` attribute (see `GatingSignal_Projections` for additional + details). + function : Function or method : default Linear specifies the function used to determine the value of the GatingSignal from the value of its `owner `. @@ -419,7 +424,7 @@ def __init__(self, size=None, transfer_function=None, modulation:tc.optional(str)=None, - modulates=None, + gate=None, params=None, name=None, prefs:is_pref_set=None, @@ -430,13 +435,39 @@ def __init__(self, # Consider adding self to owner.output_ports here (and removing from GatingProjection._instantiate_sender) # (test for it, and create if necessary, as per OutputPorts in GatingProjection._instantiate_sender), + + # Deal with **modulates** if specified + if MODULATES in kwargs: + # Don't allow **control** and **modulates** to both be specified + if gate: + raise GatingSignalError(f"Both 'gate' and '{MODULATES}' arguments are specified in the " + f"constructor for '{name if name else self.__class__.__name__}; " + f"Should use just 'gate'.") + # warnings.warn(f"The '{MODULATES}' argument (specified in the constructor for " + # f"'{name if name else self.__class__.__name__}') has been deprecated; " + # f"should use '{'control'}' going forward.") + + if PROJECTIONS in kwargs: + raise GatingSignalError(f"Both '{MODULATES}' and '{PROJECTIONS}' arguments are specified " + f"in the constructor for '{name if name else self.__class__.__name__}; " + f"Should use just '{PROJECTIONS}' (or 'gate') ") + gate = kwargs.pop(MODULATES) + + elif PROJECTIONS in kwargs: + # Don't allow **control** and **modulates** to both be specified + if gate: + raise GatingSignalError(f"Both 'gate' and '{PROJECTIONS}' arguments are specified " + f"in the constructor for '{name if name else self.__class__.__name__}; " + f"Must use just one or the other.") + + # Validate sender (as variable) and params super().__init__(owner=owner, reference_value=reference_value, default_allocation=default_allocation, size=size, modulation=modulation, - modulates=modulates, + control=gate, params=params, name=name, prefs=prefs, @@ -458,11 +489,27 @@ def _parse_port_specific_specs(self, owner, port_dict, port_specific_spec): params_dict = {} port_spec = port_specific_spec + # MODIFIED 1/2/22 NEW: if isinstance(port_specific_spec, dict): + # Note: if GATE is specified alone, it is moved to PROJECTIONS in Port._parse_ort_spec() + if GATE in port_specific_spec and PROJECTIONS in port_specific_spec: + raise GatingSignalError(f"Both 'PROJECTIONS' and 'GATE' entries found in specification dict " + f"for '{port_dict['port_type'].__name__}' of '{owner.name}'. " + f"Must use only one or the other.") return None, port_specific_spec + # MODIFIED 1/2/22 END elif isinstance(port_specific_spec, tuple): port_spec = None + # Resolve CONTROL as synonym for PROJECTIONS: + if GATE in params_dict: + # CONTROL AND PROJECTIONS can't both be used + if PROJECTIONS in params_dict: + raise GatingSignalError(f"Both 'PROJECTIONS' and 'GATE' entries found in specification dict " + f"for '{port_dict['port_type'].__name__}' of '{owner.name}'. " + f"Must use only one or the other.") + # Move GATE to PROJECTIONS + params_dict[PROJECTIONS] = params_dict.pop(GATE) params_dict[PROJECTIONS] = _parse_connection_specs(connectee_port_type=self, owner=owner, connections=port_specific_spec) diff --git a/psyneulink/core/components/ports/modulatorysignals/modulatorysignal.py b/psyneulink/core/components/ports/modulatorysignals/modulatorysignal.py index 697ee0ef062..deb1e474258 100644 --- a/psyneulink/core/components/ports/modulatorysignals/modulatorysignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/modulatorysignal.py @@ -410,7 +410,7 @@ from psyneulink.core.globals.context import ContextFlags from psyneulink.core.globals.defaults import defaultModulatoryAllocation from psyneulink.core.globals.keywords import \ - ADDITIVE_PARAM, DISABLE, MAYBE, MECHANISM, MODULATION, MODULATORY_SIGNAL, MULTIPLICATIVE_PARAM, \ + ADDITIVE_PARAM, CONTROL, DISABLE, MAYBE, MECHANISM, MODULATION, MODULATORY_SIGNAL, MULTIPLICATIVE_PARAM, \ OVERRIDE, PROJECTIONS, VARIABLE from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index 811bcb49c77..c481dda8d3c 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -584,20 +584,23 @@ print(control_signal.name) for control_projection in control_signal.efferents: print("\t{}: {}".format(control_projection.receiver.owner.name, control_projection.receiver)) - > MY DDM DRIFT RATE AND THREHOLD CONTROL SIGNAL + > MY DDM DRIFT RATE AND THRESHOLD CONTROL SIGNAL > MY DDM: (ParameterPort drift_rate) > MY DDM: (ParameterPort threshold) Note that a ControlMechanism uses a **control_signals** argument in place of an **output_ports** argument (since it -uses `ControlSignal ` for its `OutputPorts `. In the example above, -both ControlProjections are assigned to a single ControlSignal. However, they could each be assigned to their own by -specifying them in separate itesm of the **control_signals** argument:: +uses `ControlSignal ` for its `OutputPorts `. Note also that, for specifying Projections +of a ControlSignal (i.e., its ControlProjections), the keyword *CONTROL* can be used in place of the more generic +*PROJECTIONS* keyword (as shown in the example below). + +In the example above, both ControlProjections are assigned to a single ControlSignal. However, they could each be +assigned to their own by specifying them in separate items of the **control_signals** argument:: my_mech = pnl.DDM(name='MY DDM') my_ctl_mech = pnl.ControlMechanism(control_signals=[{pnl.NAME: 'DRIFT RATE CONTROL SIGNAL', - pnl.PROJECTIONS: [my_mech.parameter_ports[pnl.DRIFT_RATE]]}, + pnl.CONTROL: [my_mech.parameter_ports[pnl.DRIFT_RATE]]}, {pnl.NAME: 'THRESHOLD RATE CONTROL SIGNAL', - pnl.PROJECTIONS: [my_mech.parameter_ports[pnl.THRESHOLD]]}]) + pnl.CONTROL: [my_mech.parameter_ports[pnl.THRESHOLD]]}]) # Print ControlSignals and their ControlProjections... > DRIFT RATE CONTROL SIGNAL > MY DDM: (ParameterPort drift_rate) @@ -785,7 +788,7 @@ def test_multiple_modulatory_projections_with_mech_and_port_Name_specs(self): from psyneulink.core.globals.context import ContextFlags, handle_external_context from psyneulink.core.globals.keywords import \ ADDITIVE, ADDITIVE_PARAM, AUTO_ASSIGN_MATRIX, \ - CONTEXT, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, DEFERRED_INITIALIZATION, DISABLE, EXPONENT, \ + CONTEXT, CONTROL, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, DEFERRED_INITIALIZATION, DISABLE, EXPONENT, \ FUNCTION, FUNCTION_PARAMS, GATING_PROJECTION_PARAMS, GATING_SIGNAL_SPECS, INPUT_PORTS, \ LEARNING_PROJECTION_PARAMS, LEARNING_SIGNAL_SPECS, \ MATRIX, MECHANISM, MODULATORY_PROJECTION, MODULATORY_PROJECTIONS, MODULATORY_SIGNAL, \ @@ -1020,6 +1023,7 @@ def __init__(self, This is used by subclasses to implement the InputPort(s), OutputPort(s), and ParameterPort(s) of a Mechanism. + COMMENT: [OLD] Arguments: - owner (Mechanism): Mechanism with which Port is associated (default: NotImplemented) @@ -1050,6 +1054,7 @@ def __init__(self, NOTES: * these are used for dictionary specification of a Port in param declarations * they take precedence over arguments specified directly in the call to __init__() + COMMENT """ if kwargs: try: diff --git a/psyneulink/core/globals/parameters.py b/psyneulink/core/globals/parameters.py index feabfa83a85..27ffe9efcf0 100644 --- a/psyneulink/core/globals/parameters.py +++ b/psyneulink/core/globals/parameters.py @@ -102,8 +102,10 @@ class Parameters(A.Parameters): - an instance of *B*.Parameters will be assigned to the parameters attribute of the class *B* and all instances of *B* - each attribute on *B*.Parameters becomes a parameter (instance of the Parameter class) - as with *p*, specifying only a value uses default values for the attributes of the Parameter - - as with *q*, specifying an explicit instance of the Parameter class allows you to modify the `Parameter attributes ` -- if you want assignments to parameter *p* to be validated, add a method _validate_p(value), that returns None if value is a valid assignment, or an error string if value is not a valid assignment + - as with *q*, specifying an explicit instance of the Parameter class allows you to modify the + `Parameter attributes ` +- if you want assignments to parameter *p* to be validated, add a method _validate_p(value), + that returns None if value is a valid assignment, or an error string if value is not a valid assignment - if you want all values set to *p* to be parsed beforehand, add a method _parse_p(value) that returns the parsed value - for example, convert to a numpy array or float @@ -291,17 +293,18 @@ def _recurrent_transfer_mechanism_matrix_setter(value, owning_component=None, co import copy import itertools import logging -import toposort import types import typing import weakref +import toposort -from psyneulink.core.rpc.graph_pb2 import Entry, ndArray from psyneulink.core.globals.context import Context, ContextError, ContextFlags, _get_time, handle_external_context from psyneulink.core.globals.context import time as time_object from psyneulink.core.globals.log import LogCondition, LogEntry, LogError -from psyneulink.core.globals.utilities import call_with_pruned_args, copy_iterable_with_shared, get_alias_property_getter, get_alias_property_setter, get_deepcopy_with_shared, unproxy_weakproxy, create_union_set +from psyneulink.core.globals.utilities import call_with_pruned_args, copy_iterable_with_shared, \ + get_alias_property_getter, get_alias_property_setter, get_deepcopy_with_shared, unproxy_weakproxy, create_union_set +from psyneulink.core.rpc.graph_pb2 import Entry, ndArray __all__ = [ 'Defaults', 'get_validator_by_function', 'Parameter', 'ParameterAlias', 'ParameterError', diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index fe6aec55741..8161ab97cac 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -31,7 +31,7 @@ from psyneulink.core.globals.context import Context from psyneulink.core.globals.keywords import \ ADDITIVE, ALLOCATION_SAMPLES, BEFORE, DEFAULT, DISABLE, INPUT_PORT, INTERCEPT, LEARNING_MECHANISMS, \ - LEARNED_PROJECTIONS, RANDOM_CONNECTIVITY_MATRIX, \ + LEARNED_PROJECTIONS, RANDOM_CONNECTIVITY_MATRIX, CONTROL, \ NAME, PROJECTIONS, RESULT, OBJECTIVE_MECHANISM, OUTPUT_MECHANISM, OVERRIDE, SLOPE, TARGET_MECHANISM, VARIANCE from psyneulink.core.scheduling.condition import AtTimeStep, AtTrial, Never, TimeInterval from psyneulink.core.scheduling.condition import EveryNCalls @@ -1264,7 +1264,8 @@ def test_composition_learning_pathway_dict_with_no_learning_fct_in_tuple_error(s class TestProperties: - def test_properties(self): + @pytest.mark.parametrize("control_spec", [CONTROL, PROJECTIONS]) + def test_properties(self, control_spec): Input = pnl.TransferMechanism(name='Input') Reward = pnl.TransferMechanism(output_ports=[pnl.RESULT, pnl.MEAN, pnl.VARIANCE], name='reward') @@ -1286,11 +1287,11 @@ def test_properties(self): Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], Decision.output_ports[pnl.RESPONSE_TIME]], function=pnl.GridSearch(), - control_signals=[{PROJECTIONS: ("drift_rate", Decision), + control_signals=[{control_spec: ("drift_rate", Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {PROJECTIONS: ("threshold", Decision), + {control_spec: ("threshold", Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {PROJECTIONS: ("slope", Reward), + {control_spec: ("slope", Reward), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}])) assert len(comp.nodes) == len(comp.mechanisms) == 3 diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 3df12dd9dd0..dc3713a3883 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -4,7 +4,7 @@ import pytest import psyneulink as pnl -from psyneulink.core.globals.keywords import ALLOCATION_SAMPLES, PROJECTIONS +from psyneulink.core.globals.keywords import ALLOCATION_SAMPLES, CONTROL, PROJECTIONS from psyneulink.core.globals.log import LogCondition from psyneulink.core.globals.sampleiterator import SampleIterator, SampleIteratorError, SampleSpec from psyneulink.core.globals.utilities import _SeededPhilox @@ -94,7 +94,8 @@ def test_redundant_control_spec_add_controller_in_comp_constructor_then_add_node assert ddm.parameter_ports['drift_rate'].mod_afferents[0].sender.owner == comp.controller assert comp.controller.control_signals[0].allocation_samples is None - def test_redundant_control_spec_add_controller_in_comp_constructor_then_add_node_with_alloc_samples_specified(self): + @pytest.mark.parametrize("control_spec", [CONTROL, PROJECTIONS]) + def test_redundant_control_spec_add_controller_in_comp_constructor_then_add_node_with_alloc_samples_specified(self,control_spec): # First create Composition with controller that has HAS control specification that includes allocation_samples, # then add Mechanism with control specification to Composition; # Control specification on controller should supercede one on Mechanism (which should be ignored) @@ -108,7 +109,7 @@ def test_redundant_control_spec_add_controller_in_comp_constructor_then_add_node "deactivated until 'DDM-0' is added to' Composition-0' in a compatible way." with pytest.warns(UserWarning, match=expected_warning): comp = pnl.Composition(controller=pnl.ControlMechanism(control_signals={ALLOCATION_SAMPLES:np.arange(0.2,1.01, 0.3), - PROJECTIONS:('drift_rate', ddm)})) + control_spec:('drift_rate', ddm)})) comp.add_node(ddm) assert comp.controller.control_signals[0].efferents[0].receiver == ddm.parameter_ports['drift_rate'] assert ddm.parameter_ports['drift_rate'].mod_afferents[0].sender.owner == comp.controller @@ -138,7 +139,8 @@ def test_objective_mechanism_spec_as_monitor_for_control_error(self): error_msg = error.value.error_value assert expected_error in error_msg - def test_deferred_init(self): + @pytest.mark.parametrize("control_spec", [CONTROL, PROJECTIONS]) + def test_deferred_init(self, control_spec): # Test to insure controller works the same regardless of whether it is added to a composition before or after # the nodes it connects to @@ -182,9 +184,9 @@ def test_deferred_init(self): Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), function=pnl.GridSearch(), - control_signals=[{PROJECTIONS: ("drift_rate", Decision), + control_signals=[{control_spec: ("drift_rate", Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {PROJECTIONS: ("threshold", Decision), + {control_spec: ("threshold", Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) ) assert comp._controller_initialization_status == pnl.ContextFlags.DEFERRED_INIT @@ -263,7 +265,7 @@ def test_partial_deferred_init(self): pathways=[initial_node_a, initial_node_b], controller_mode=pnl.BEFORE) - member_node_control_signal = pnl.ControlSignal(projections=[(pnl.SLOPE, initial_node_a)], + member_node_control_signal = pnl.ControlSignal(control=[(pnl.SLOPE, initial_node_a)], variable=1.0, intensity_cost_function=pnl.Linear(slope=0.0), allocation_samples=pnl.SampleSpec(start=1.0, @@ -821,9 +823,9 @@ def test_lvoc(self): monitor=[m1, m2]), function=pnl.GridSearch(max_iterations=1), control_signals=[ - {PROJECTIONS: (pnl.SLOPE, m1), + {CONTROL: (pnl.SLOPE, m1), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {PROJECTIONS: (pnl.SLOPE, m2), + {CONTROL: (pnl.SLOPE, m2), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) c.add_node(lvoc) input_dict = {m1: [[1], [1]], m2: [1]} @@ -845,9 +847,9 @@ def test_lvoc_both_predictors_specs(self): monitor=[m1, m2]), function=pnl.GridSearch(max_iterations=1), control_signals=[ - {PROJECTIONS: (pnl.SLOPE, m1), + {CONTROL: (pnl.SLOPE, m1), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {PROJECTIONS: (pnl.SLOPE, m2), + {CONTROL: (pnl.SLOPE, m2), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) c.add_node(lvoc) input_dict = {m1: [[1], [1]], m2: [1]} @@ -1651,9 +1653,9 @@ def test_evc(self): Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), function=pnl.GridSearch(), - control_signals=[{PROJECTIONS: ("drift_rate", Decision), + control_signals=[{CONTROL: ("drift_rate", Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {PROJECTIONS: ("threshold", Decision), + {CONTROL: ("threshold", Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) ) @@ -1940,11 +1942,11 @@ def test_laming_validation_specify_control_signals(self): function=pnl.GridSearch(), control_signals=[ { - PROJECTIONS: (pnl.DRIFT_RATE, Decision), + CONTROL: (pnl.DRIFT_RATE, Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3) }, { - PROJECTIONS: (pnl.THRESHOLD, Decision), + CONTROL: (pnl.THRESHOLD, Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3) } ], @@ -2078,11 +2080,11 @@ def test_stateful_mechanism_in_simulation(self): function=pnl.GridSearch(), control_signals=[ { - PROJECTIONS: (pnl.DRIFT_RATE, Decision), + CONTROL: (pnl.DRIFT_RATE, Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3) }, { - PROJECTIONS: (pnl.THRESHOLD, Decision), + CONTROL: (pnl.THRESHOLD, Decision), ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3) } ], diff --git a/tests/mechanisms/test_control_mechanism.py b/tests/mechanisms/test_control_mechanism.py index 916dee927b7..f43fdaf04b4 100644 --- a/tests/mechanisms/test_control_mechanism.py +++ b/tests/mechanisms/test_control_mechanism.py @@ -257,11 +257,12 @@ def test_control_signal_default_allocation_specification(self): name='C3', default_variable=[10], default_allocation=[4], - control_signals=[pnl.ControlSignal(modulates=(pnl.SLOPE, m1)), # tests for assignment to default_allocation + # Test synonyms allowed for **control**: generic **modulates**, and even more generic **projections**): + control_signals=[pnl.ControlSignal(control=(pnl.SLOPE, m1)), # tests for assignment to default_allocation pnl.ControlSignal(default_allocation=5, # tests for override of default_allocation modulates=(pnl.SLOPE, m2)), pnl.ControlSignal(default_allocation=[6], # as above same but with array - modulates=(pnl.SLOPE, m3))]) + projections=(pnl.SLOPE, m3))]) comp = pnl.Composition() comp.add_nodes([m1,m2,m3]) comp.add_controller(c2) diff --git a/tests/naming/test_naming.py b/tests/naming/test_naming.py index fb44f6bc44b..2082b73c47e 100644 --- a/tests/naming/test_naming.py +++ b/tests/naming/test_naming.py @@ -155,7 +155,8 @@ def test_input_port_and_assigned_projection_names(self): # TEST 11 # Test that ControlSignals and ControlProjections are properly named - def test_control_signal_and_control_projection_names(self): + @pytest.mark.parametrize('control_spec', [pnl.CONTROL, pnl.PROJECTIONS]) + def test_control_signal_and_control_projection_names(self, control_spec): D1 = pnl.DDM(name='D1') D2 = pnl.DDM(name='D2') @@ -166,7 +167,7 @@ def test_control_signal_and_control_projection_names(self): assert C1.control_signals[0].efferents[0].name == 'ControlProjection for D1[drift_rate]' # ControlSignal with two ControlProjection to two parameters of same Mechanism - C2 = pnl.ControlMechanism(control_signals=[{pnl.PROJECTIONS:[D1.parameter_ports[ + C2 = pnl.ControlMechanism(control_signals=[{control_spec:[D1.parameter_ports[ psyneulink.core.components.functions.nonstateful.distributionfunctions.DRIFT_RATE], D1.parameter_ports[ psyneulink.core.globals.keywords.THRESHOLD]]}]) @@ -175,7 +176,7 @@ def test_control_signal_and_control_projection_names(self): assert C2.control_signals[0].efferents[1].name == 'ControlProjection for D1[threshold]' # ControlSignal with two ControlProjection to two parameters of different Mechanisms - C3 = pnl.ControlMechanism(control_signals=[{pnl.PROJECTIONS:[D1.parameter_ports[ + C3 = pnl.ControlMechanism(control_signals=[{control_spec:[D1.parameter_ports[ psyneulink.core.components.functions.nonstateful.distributionfunctions.DRIFT_RATE], D2.parameter_ports[ psyneulink.core.components.functions.nonstateful.distributionfunctions.DRIFT_RATE]]}]) diff --git a/tests/projections/test_projection_specifications.py b/tests/projections/test_projection_specifications.py index bd3dd78fb7b..52f39ff5ac0 100644 --- a/tests/projections/test_projection_specifications.py +++ b/tests/projections/test_projection_specifications.py @@ -1,16 +1,17 @@ -import psyneulink as pnl import numpy as np import pytest +import psyneulink as pnl import psyneulink.core.components.functions.nonstateful.distributionfunctions -import psyneulink.core.components.functions.stateful.integratorfunctions import psyneulink.core.components.functions.nonstateful.transferfunctions +import psyneulink.core.components.functions.stateful.integratorfunctions + class TestProjectionSpecificationFormats: def test_projection_specification_formats(self): """Test various matrix and Projection specifications - Also tests assignment of Projections to pathay of Composition using add_linear_processing_pathway: + Also tests assignment of Projections to pathway of Composition using add_linear_processing_pathway: - Projection explicitly specified in sequence (M1_M2_proj) - Projection pre-constructed and assigned to Mechanisms, but not specified in pathway(M2_M3_proj) - Projection specified in pathway that is duplicate one preconstructed and assigned to Mechanisms (M3_M4_proj) @@ -52,25 +53,112 @@ def test_projection_specification_formats(self): # assert np.allclose(c.results, [[-130.19166667, -152.53333333, -174.875]]) assert np.allclose(c.results, [[ -78.115, -91.52 , -104.925]]) - def test_multiple_modulatory_projection_specs(self): + @pytest.mark.parametrize('args', [ + (pnl.CONTROL, None), + (pnl.MODULATES, None), + (pnl.PROJECTIONS, None), + ('mod and ctl', '"Both \'control\' and \'modulates\' arguments are specified in ' + 'the constructor for \'ControlSignal; Should use just \'control\'."'), + ('proj and ctl', 'Both \'control\' and \'projections\' arguments are specified in the constructor for ' + '\'ControlSignal; Must use just one or the other.'), + ('proj and mod','"Both \'modulates\' and \'projections\' arguments are specified in the constructor for ' + '\'ControlSignal; Should use just \'projections\' (or \'control\') "') + ]) + def test_control_signal_projections_arg(self, args): + M = pnl.ProcessingMechanism() + control_specs = {pnl.CONTROL: {'control':(pnl.SLOPE, M)}, + pnl.MODULATES: {pnl.MODULATES:(pnl.SLOPE, M)}, + pnl.PROJECTIONS: {pnl.PROJECTIONS:(pnl.SLOPE, M)}, + 'mod and ctl': {'control':(pnl.SLOPE, M), + pnl.MODULATES:(pnl.SLOPE, M)}, + 'proj and ctl': {'control':(pnl.SLOPE, M), + pnl.PROJECTIONS:(pnl.SLOPE, M)}, + 'proj and mod': {pnl.MODULATES:(pnl.SLOPE, M), + pnl.PROJECTIONS:(pnl.SLOPE, M)} + } + if args[0] in {'mod and ctl', 'proj and ctl', 'proj and mod'}: + from psyneulink.core.components.ports.modulatorysignals.controlsignal import ControlSignalError + with pytest.raises(ControlSignalError) as err: + pnl.ControlSignal(**control_specs[args[0]]) + assert args[1] in str(err.value) + else: + ctl_sig = pnl.ControlSignal(**control_specs[args[0]]) + assert ctl_sig._init_args[pnl.PROJECTIONS][0][0] == pnl.SLOPE + assert ctl_sig._init_args[pnl.PROJECTIONS][0][1] is M + + @pytest.mark.parametrize('args', [ + (pnl.GATE, None), + (pnl.MODULATES, None), + (pnl.PROJECTIONS, None), + ('mod and gate', '"Both \'gate\' and \'modulates\' arguments are specified in the constructor for ' + '\'GatingSignal; Should use just \'gate\'."'), + ('proj and gate', 'Both \'gate\' and \'projections\' arguments are specified in the constructor for ' + '\'GatingSignal; Must use just one or the other.'), + ('proj and mod','"Both \'modulates\' and \'projections\' arguments are specified in the constructor for ' + '\'GatingSignal; Should use just \'projections\' (or \'gate\') "') + ]) + def test_gating_signal_projections_arg(self, args): + M = pnl.ProcessingMechanism() + gating_specs = {pnl.GATE: {'gate':(pnl.SLOPE, M)}, + pnl.MODULATES: {pnl.MODULATES:(pnl.SLOPE, M)}, + pnl.PROJECTIONS: {pnl.PROJECTIONS:(pnl.SLOPE, M)}, + 'mod and gate': {'gate':(pnl.SLOPE, M), + pnl.MODULATES:(pnl.SLOPE, M)}, + 'proj and gate': {'gate':(pnl.SLOPE, M), + pnl.PROJECTIONS:(pnl.SLOPE, M)}, + 'proj and mod': {pnl.MODULATES:(pnl.SLOPE, M), + pnl.PROJECTIONS:(pnl.SLOPE, M)} + } + if args[0] in {'mod and gate', 'proj and gate', 'proj and mod'}: + from psyneulink.core.components.ports.modulatorysignals.gatingsignal import GatingSignalError + with pytest.raises(GatingSignalError) as err: + pnl.GatingSignal(**gating_specs[args[0]]) + assert args[1] in str(err.value) + else: + gating_sig = pnl.GatingSignal(**gating_specs[args[0]]) + assert gating_sig._init_args[pnl.PROJECTIONS][0][0] == pnl.SLOPE + assert gating_sig._init_args[pnl.PROJECTIONS][0][1] is M + + @pytest.mark.parametrize("control_spec, gating_spec, extra_spec", + [ + [pnl.CONTROL, pnl.GATE, ''], + [pnl.PROJECTIONS, pnl.PROJECTIONS, ''], + [pnl.CONTROL, pnl.GATE, pnl.PROJECTIONS] + ] + ) + def test_multiple_modulatory_projection_specs(self, control_spec, gating_spec, extra_spec): M = pnl.DDM(name='MY DDM') - C = pnl.ControlMechanism(control_signals=[{pnl.PROJECTIONS: [M.parameter_ports[ - psyneulink.core.components.functions.nonstateful.distributionfunctions.DRIFT_RATE], - M.parameter_ports[ - psyneulink.core.globals.keywords.THRESHOLD]]}]) - G = pnl.GatingMechanism(gating_signals=[{pnl.PROJECTIONS: [M.output_ports[pnl.DECISION_VARIABLE], - M.output_ports[pnl.RESPONSE_TIME]]}]) - assert len(C.control_signals)==1 - assert len(C.control_signals[0].efferents)==2 - assert M.parameter_ports[ - psyneulink.core.components.functions.nonstateful.distributionfunctions.DRIFT_RATE].mod_afferents[0] == C.control_signals[0].efferents[0] - assert M.parameter_ports[ - psyneulink.core.globals.keywords.THRESHOLD].mod_afferents[0] == C.control_signals[0].efferents[1] - assert len(G.gating_signals)==1 - assert len(G.gating_signals[0].efferents)==2 - assert M.output_ports[pnl.DECISION_VARIABLE].mod_afferents[0]==G.gating_signals[0].efferents[0] - assert M.output_ports[pnl.RESPONSE_TIME].mod_afferents[0]==G.gating_signals[0].efferents[1] + ctl_sig_spec = {control_spec: [M.parameter_ports[pnl.DRIFT_RATE], + M.parameter_ports[pnl.THRESHOLD]]} + gating_sig_spec = {gating_spec: [M.output_ports[pnl.DECISION_VARIABLE], + M.output_ports[pnl.RESPONSE_TIME]]} + if extra_spec: + ctl_sig_spec.update({extra_spec:[M.parameter_ports[pnl.STARTING_POINT]]}) + gating_sig_spec.update({extra_spec:[M.output_ports[pnl.RESPONSE_TIME]]}) + ctl_err_msg = '"Both \'PROJECTIONS\' and \'CONTROL\' entries found in specification dict for ' \ + '\'ControlSignal\' of \'ControlMechanism-0\'. Must use only one or the other."' + with pytest.raises(pnl.ControlSignalError) as err: + pnl.ControlMechanism(control_signals=[ctl_sig_spec]) + assert ctl_err_msg == str(err.value) + gating_err_msg = '"Both \'PROJECTIONS\' and \'GATE\' entries found in specification dict for ' \ + '\'GatingSignal\' of \'GatingMechanism-0\'. Must use only one or the other."' + with pytest.raises(pnl.GatingSignalError) as err: + pnl.GatingMechanism(gating_signals=[gating_sig_spec]) + assert gating_err_msg == str(err.value) + else: + C = pnl.ControlMechanism(control_signals=[ctl_sig_spec]) + G = pnl.GatingMechanism(gating_signals=[gating_sig_spec]) + assert len(C.control_signals)==1 + assert len(C.control_signals[0].efferents)==2 + assert M.parameter_ports[ + psyneulink.core.components.functions.nonstateful.distributionfunctions.DRIFT_RATE].mod_afferents[0] == C.control_signals[0].efferents[0] + assert M.parameter_ports[ + psyneulink.core.globals.keywords.THRESHOLD].mod_afferents[0] == C.control_signals[0].efferents[1] + assert len(G.gating_signals)==1 + assert len(G.gating_signals[0].efferents)==2 + assert M.output_ports[pnl.DECISION_VARIABLE].mod_afferents[0]==G.gating_signals[0].efferents[0] + assert M.output_ports[pnl.RESPONSE_TIME].mod_afferents[0]==G.gating_signals[0].efferents[1] def test_multiple_modulatory_projections_with_port_Name(self): From 491fedea96daf89d14045746edc96594e19dac83 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Mon, 3 Jan 2022 17:10:53 -0500 Subject: [PATCH 081/285] Refactor/keyword control to control (#2273) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • controlmechanism.py, gatingmechanism.py: - CONTROL: 'CONTROL' -> 'control' - GATING: 'GATING' -> 'gating' * • Project: - CONTROL: 'CONTROL' -> 'control' - del GATING - GATE = 'gate" Co-authored-by: jdcpni --- .../Examples/Gating-Mechanism. with UDF.py | 4 ++- .../modulatory/control/controlmechanism.py | 26 +++++++++++-------- .../control/gating/gatingmechanism.py | 19 +++++++++++--- .../ports/modulatorysignals/controlsignal.py | 8 +++--- .../modulatory/gatingprojection.py | 6 ++--- .../core/components/projections/projection.py | 10 +++---- psyneulink/core/globals/keywords.py | 10 +++---- tests/composition/test_gating.py | 3 ++- tests/mechanisms/test_gating_mechanism.py | 9 ++++--- .../test_projection_specifications.py | 10 ++++--- 10 files changed, 63 insertions(+), 42 deletions(-) diff --git a/Scripts/Examples/Gating-Mechanism. with UDF.py b/Scripts/Examples/Gating-Mechanism. with UDF.py index bf8e94f03f3..91d51d3fc83 100644 --- a/Scripts/Examples/Gating-Mechanism. with UDF.py +++ b/Scripts/Examples/Gating-Mechanism. with UDF.py @@ -1,5 +1,7 @@ import functools + import numpy as np + import psyneulink as pnl import psyneulink.core.components.functions.nonstateful.transferfunctions @@ -51,7 +53,7 @@ def my_sinusoidal_fct(input, # pnl.FUNCTION: my_sinusoidal_fct} output_ports={pnl.NAME: 'RESULTS USING UDF', # pnl.VARIABLE: (pnl.OWNER_VALUE, 0), - pnl.FUNCTION: psyneulink.core.components.functions.nonstateful.transferfunctions.Linear(slope=pnl.GATING) + pnl.FUNCTION: psyneulink.core.components.functions.nonstateful.transferfunctions.Linear(slope=pnl.GATE) # pnl.FUNCTION: pnl.Logistic(gain=pnl.GATING) # pnl.FUNCTION: my_linear_fct # pnl.FUNCTION: my_exp_fct diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index 27743dabdea..4e873865a34 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1206,8 +1206,8 @@ class Parameters(ModulatoryMechanism_Base.Parameters): read_only=True, structural=True, parse_spec=True, - aliases=['control', 'control_signals'], - constructor_argument='control' + aliases=[CONTROL, CONTROL_SIGNALS], + constructor_argument=CONTROL ) # MODIFIED 1/2/22 OLD: - MUCH OF THIS SEEMS TO BE COVERED ELSEWHERE; COMMENTING OUT ONLY CAUSES PROBLEMS WITH @@ -1308,7 +1308,7 @@ def __init__(self, # Only allow one of CONTROL, MODULATORY_SIGNALS OR CONTROL_SIGNALS to be specified # These are synonyms, but allowing several to be specified and trying to combine the specifications # can cause problems if different forms of specification are used to refer to the same Component(s) - control_specified = "'control'" if control else '' + control_specified = f"'{CONTROL}'" if control else '' modulatory_signals_specified = '' if MODULATORY_SIGNALS in kwargs: args = kwargs.pop(MODULATORY_SIGNALS) @@ -1412,14 +1412,18 @@ def _validate_params(self, request_set, target_set=None, context=None): if CONTROL in target_set and target_set[CONTROL]: control = target_set[CONTROL] - assert isinstance(control, list), \ - f"PROGRAM ERROR: control arg {control} of {self.name} should have been converted to a list." - for ctl_spec in control: - ctl_spec = _parse_port_spec(port_type=ControlSignal, owner=self, port_spec=ctl_spec) - if not (isinstance(ctl_spec, ControlSignal) - or (isinstance(ctl_spec, dict) and ctl_spec[PORT_TYPE]==ControlSignal.__name__)): - raise ControlMechanismError(f"Invalid specification for '{CONTROL}' argument of {self.name}:" - f"({ctl_spec})") + self._validate_control_arg(control) + + def _validate_control_arg(self, control): + """Treat control arg separately so it can be overridden by subclassses (e.g., GatingMechanism)""" + assert isinstance(control, list), \ + f"PROGRAM ERROR: control arg {control} of {self.name} should have been converted to a list." + for ctl_spec in control: + ctl_spec = _parse_port_spec(port_type=ControlSignal, owner=self, port_spec=ctl_spec) + if not (isinstance(ctl_spec, ControlSignal) + or (isinstance(ctl_spec, dict) and ctl_spec[PORT_TYPE] == ControlSignal)): + raise ControlMechanismError(f"Invalid specification for '{CONTROL}' argument of {self.name}:" + f"({ctl_spec})") # IMPLEMENTATION NOTE: THIS SHOULD BE MOVED TO COMPOSITION ONCE THAT IS IMPLEMENTED def _instantiate_objective_mechanism(self, input_ports=None, context=None): diff --git a/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py index ed919e5bb67..61cea19174d 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py @@ -185,10 +185,11 @@ from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import ControlMechanism from psyneulink.core.components.ports.modulatorysignals.gatingsignal import GatingSignal +from psyneulink.core.components.ports.port import _parse_port_spec from psyneulink.core.globals.defaults import defaultGatingAllocation from psyneulink.core.globals.keywords import \ - GATE, GATING, GATING_PROJECTION, GATING_SIGNAL, GATING_SIGNALS, \ - INIT_EXECUTE_METHOD_ONLY, MONITOR_FOR_CONTROL, PROJECTION_TYPE + CONTROL, CONTROL_SIGNALS, GATE, GATING_PROJECTION, GATING_SIGNAL, GATING_SIGNALS, \ + INIT_EXECUTE_METHOD_ONLY, MONITOR_FOR_CONTROL, PORT_TYPE, PROJECTION_TYPE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel @@ -217,7 +218,7 @@ def _is_gating_spec(spec): GatingMechanism, ControlMechanism)): return True - elif isinstance(spec, str) and spec in {GATING, GATING_PROJECTION, GATING_SIGNAL}: + elif isinstance(spec, str) and spec in {GATE, GATING_PROJECTION, GATING_SIGNAL}: return True else: return False @@ -427,7 +428,7 @@ class Parameters(ControlMechanism.Parameters): read_only=True, structural=True, parse_spec=True, - aliases=['control', 'control_signals', 'gate', 'gating_signal'], + aliases=[CONTROL, CONTROL_SIGNALS, 'gate', 'gating_signal'], constructor_argument='gate' ) @@ -526,6 +527,16 @@ def _register_control_signal_type(self, context=None): registry=self._portRegistry, ) + def _validate_control_arg(self, gate): + """Overrided to handle GatingMechanism-specific specifications""" + assert isinstance(gate, list), \ + f"PROGRAM ERROR: 'gate' arg ({gate}) of {self.name} should have been converted to a list." + for spec in gate: + spec = _parse_port_spec(port_type=GatingSignal, owner=self, port_spec=spec) + if not (isinstance(spec, GatingSignal) + or (isinstance(spec, dict) and spec[PORT_TYPE] == GatingSignal)): + raise GatingMechanismError(f"Invalid specification for '{GATE}' argument of {self.name}: ({spec})") + def _instantiate_control_signal_type(self, gating_signal_spec, context): """Instantiate actual ControlSignal, or subclass if overridden""" from psyneulink.core.components.ports.port import _instantiate_port diff --git a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py index a2ed0953f72..3ddd61cffbc 100644 --- a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py @@ -822,9 +822,9 @@ def __init__(self, if MODULATES in kwargs: # Don't allow **control** and **modulates** to both be specified if control: - raise ControlSignalError(f"Both 'control' and '{MODULATES}' arguments are specified in the " + raise ControlSignalError(f"Both '{CONTROL}' and '{MODULATES}' arguments are specified in the " f"constructor for '{name if name else self.__class__.__name__}; " - f"Should use just 'control'.") + f"Should use just '{CONTROL}'.") # warnings.warn(f"The '{MODULATES}' argument (specified in the constructor for " # f"'{name if name else self.__class__.__name__}') has been deprecated; " # f"should use '{'control'}' going forward.") @@ -832,13 +832,13 @@ def __init__(self, if PROJECTIONS in kwargs: raise ControlSignalError(f"Both '{MODULATES}' and '{PROJECTIONS}' arguments are specified " f"in the constructor for '{name if name else self.__class__.__name__}; " - f"Should use just '{PROJECTIONS}' (or 'control') ") + f"Should use just '{PROJECTIONS}' (or '{CONTROL}') ") control = kwargs.pop(MODULATES) elif PROJECTIONS in kwargs: # Don't allow **control** and **modulates** to both be specified if control: - raise ControlSignalError(f"Both 'control' and '{PROJECTIONS}' arguments are specified " + raise ControlSignalError(f"Both '{CONTROL}' and '{PROJECTIONS}' arguments are specified " f"in the constructor for '{name if name else self.__class__.__name__}; " f"Must use just one or the other.") diff --git a/psyneulink/core/components/projections/modulatory/gatingprojection.py b/psyneulink/core/components/projections/modulatory/gatingprojection.py index 7b810775f4b..1c852bbea2c 100644 --- a/psyneulink/core/components/projections/modulatory/gatingprojection.py +++ b/psyneulink/core/components/projections/modulatory/gatingprojection.py @@ -110,7 +110,7 @@ from psyneulink.core.components.shellclasses import Mechanism, Process_Base from psyneulink.core.globals.context import ContextFlags from psyneulink.core.globals.keywords import \ - FUNCTION_OUTPUT_TYPE, GATING, GATING_MECHANISM, GATING_PROJECTION, GATING_SIGNAL, \ + FUNCTION_OUTPUT_TYPE, GATE, GATING_MECHANISM, GATING_PROJECTION, GATING_SIGNAL, \ INPUT_PORT, OUTPUT_PORT from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set @@ -120,8 +120,8 @@ 'GATING_SIGNAL_PARAMS', 'GatingProjection', 'GatingProjectionError', ] -parameter_keywords.update({GATING_PROJECTION, GATING}) -projection_keywords.update({GATING_PROJECTION, GATING}) +parameter_keywords.update({GATING_PROJECTION, GATE}) +projection_keywords.update({GATING_PROJECTION, GATE}) GATING_SIGNAL_PARAMS = 'gating_signal_params' class GatingProjectionError(Exception): diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 65b17b21e53..48e91ce5e49 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -133,7 +133,7 @@ as its the ControlProjection's `sender `. See `ControlMechanism_ControlSignals` for additional details. - * *GATING_PROJECTION* (or *GATING*) -- this can be used when specifying an `InputPort + * *GATING_PROJECTION* (or *GATE*) -- this can be used when specifying an `InputPort ` or an `OutputPort `, to create a default `GatingProjection` to the `Port `. If the GatingProjection's `sender ` cannot be inferred from the context in which this specification occurs, then its `initialization is deferred @@ -402,14 +402,14 @@ import typecheck as tc from psyneulink.core import llvm as pnlvm -from psyneulink.core.components.functions.nonstateful.transferfunctions import LinearMatrix from psyneulink.core.components.functions.function import get_matrix -from psyneulink.core.components.shellclasses import Mechanism, Process_Base, Projection, Port +from psyneulink.core.components.functions.nonstateful.transferfunctions import LinearMatrix from psyneulink.core.components.ports.modulatorysignals.modulatorysignal import _is_modulatory_spec from psyneulink.core.components.ports.port import PortError +from psyneulink.core.components.shellclasses import Mechanism, Process_Base, Projection, Port from psyneulink.core.globals.context import ContextFlags from psyneulink.core.globals.keywords import \ - CONTROL, CONTROL_PROJECTION, CONTROL_SIGNAL, EXPONENT, FUNCTION_PARAMS, GATING, GATING_PROJECTION, GATING_SIGNAL, \ + CONTROL, CONTROL_PROJECTION, CONTROL_SIGNAL, EXPONENT, FUNCTION_PARAMS, GATE, GATING_PROJECTION, GATING_SIGNAL, \ INPUT_PORT, LEARNING, LEARNING_PROJECTION, LEARNING_SIGNAL, \ MAPPING_PROJECTION, MATRIX, MATRIX_KEYWORD_SET, MECHANISM, \ MODEL_SPEC_ID_RECEIVER_MECH, MODEL_SPEC_ID_RECEIVER_PORT, MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_SENDER_PORT, \ @@ -443,7 +443,7 @@ CONTROL: CONTROL_PROJECTION, CONTROL_SIGNAL: CONTROL_PROJECTION, CONTROL_PROJECTION: CONTROL_PROJECTION, - GATING: GATING_PROJECTION, + GATE: GATING_PROJECTION, GATING_SIGNAL: GATING_PROJECTION, GATING_PROJECTION: GATING_PROJECTION } diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index 959cebee2b2..90cbfa7fe2f 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -52,7 +52,7 @@ 'FEEDBACK', 'FITZHUGHNAGUMO_INTEGRATOR_FUNCTION', 'FINAL', 'FLAGS', 'FULL', 'FULL_CONNECTIVITY_MATRIX', 'FUNCTION', 'FUNCTIONS', 'FUNCTION_COMPONENT_CATEGORY','FUNCTION_CHECK_ARGS', 'FUNCTION_OUTPUT_TYPE', 'FUNCTION_OUTPUT_TYPE_CONVERSION', 'FUNCTION_PARAMS', - 'GAIN', 'GAMMA_DIST_FUNCTION', 'GATE', 'GATING', 'GATING_MECHANISM', 'GATING_ALLOCATION', 'GATING_PROJECTION', + 'GAIN', 'GAMMA_DIST_FUNCTION', 'GATE', 'GATING_MECHANISM', 'GATING_ALLOCATION', 'GATING_PROJECTION', 'GATING_PROJECTION_PARAMS', 'GATING_PROJECTIONS', 'GATING_SIGNAL', 'GATING_SIGNAL_SPECS', 'GATING_SIGNALS', 'GAUSSIAN', 'GAUSSIAN_FUNCTION', 'GILZENRAT_INTEGRATOR_FUNCTION', 'GREATER_THAN', 'GREATER_THAN_OR_EQUAL', 'GRADIENT_OPTIMIZATION_FUNCTION', 'GRID_SEARCH_FUNCTION', @@ -660,8 +660,7 @@ def _is_metric(metric): PULSE_CLAMP = "pulse_clamp" NO_CLAMP = "no_clamp" LEARNING_RATE = "learning_rate" -CONTROL = 'CONTROL' -GATING = 'gating' +# CONTROL = 'CONTROL' PROCESS_DEFAULT_PROJECTION_FUNCTION = "Default Projection Function" PROCESS_EXECUTE = "ProcessExecute" MECHANISM_EXECUTED_LOG_ENTRY = "Mechanism Executed" @@ -764,6 +763,7 @@ def _is_metric(metric): PREDICTION_MECHANISM_OUTPUT = "PredictionMechanismOutput" MODULATORY_SIGNALS = 'modulatory_signals' +CONTROL = 'control' CONTROL_SIGNALS = 'control_signals' CONTROL_SIGNAL_SPECS = 'CONTROL_SIGNAL_SPECS' CONTROLLED_PARAMS = 'CONTROLLED_PARAMS' @@ -778,16 +778,16 @@ def _is_metric(metric): ALLOCATION_SAMPLES = "allocation_samples" # GatingMechanism +GATE = 'gate' GATING_SIGNALS = 'gating_signals' GATING_SIGNAL_SPECS = 'GATING_SIGNAL_SPECS' -GATE = 'GATE' GATED_PORTS = 'GATED_PORTS' GATING_PROJECTIONS = 'GatingProjections' GATING_ALLOCATION = 'gating_allocation' MODULATORY_SPEC_KEYWORDS = {LEARNING, LEARNING_SIGNAL, LEARNING_PROJECTION, LEARNING_MECHANISM, CONTROL, CONTROL_SIGNAL, CONTROL_PROJECTION, CONTROL_MECHANISM, - GATING, GATING_SIGNAL, GATING_PROJECTION, GATING_MECHANISM} + GATE, GATING_SIGNAL, GATING_PROJECTION, GATING_MECHANISM} MODULATED_PARAMETER_PREFIX = 'mod_' diff --git a/tests/composition/test_gating.py b/tests/composition/test_gating.py index 21f787a6f89..486f1c04fbb 100644 --- a/tests/composition/test_gating.py +++ b/tests/composition/test_gating.py @@ -1,5 +1,6 @@ import numpy as np import pytest + import psyneulink as pnl @@ -19,7 +20,7 @@ def test_gating(benchmark, comp_mode): function=pnl.Linear(), output_ports={ pnl.NAME: 'RESULTS USING UDF', - pnl.FUNCTION: pnl.Linear(slope=pnl.GATING) + pnl.FUNCTION: pnl.Linear(slope=pnl.GATE) } ) diff --git a/tests/mechanisms/test_gating_mechanism.py b/tests/mechanisms/test_gating_mechanism.py index a57dae02a9f..2d1a59a35eb 100644 --- a/tests/mechanisms/test_gating_mechanism.py +++ b/tests/mechanisms/test_gating_mechanism.py @@ -1,15 +1,16 @@ import numpy as np + import psyneulink as pnl import psyneulink.core.components.functions.nonstateful.transferfunctions - -from psyneulink.core.components.functions.stateful.integratorfunctions import AccumulatorIntegrator from psyneulink.core.components.functions.nonstateful.transferfunctions import Logistic +from psyneulink.core.components.functions.stateful.integratorfunctions import AccumulatorIntegrator from psyneulink.core.components.mechanisms.modulatory.control.gating.gatingmechanism import GatingMechanism from psyneulink.core.components.mechanisms.processing.transfermechanism import TransferMechanism from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection +from psyneulink.core.compositions.composition import Composition from psyneulink.core.globals.keywords import \ DEFAULT_VARIABLE, FUNCTION, FUNCTION_PARAMS, INITIALIZER, RATE, VALUE -from psyneulink.core.compositions.composition import Composition + def test_gating_with_composition(): """Tests same configuration as control of InputPort in tests/mechansims/test_identicalness_of_control_and_gating @@ -131,7 +132,7 @@ def my_sinusoidal_fct( output_ports={ pnl.NAME: 'RESULTS USING UDF', # pnl.VARIABLE: (pnl.OWNER_VALUE, 0), - pnl.FUNCTION: psyneulink.core.components.functions.nonstateful.transferfunctions.Linear(slope=pnl.GATING) + pnl.FUNCTION: psyneulink.core.components.functions.nonstateful.transferfunctions.Linear(slope=pnl.GATE) } ) diff --git a/tests/projections/test_projection_specifications.py b/tests/projections/test_projection_specifications.py index 52f39ff5ac0..583241c7aa0 100644 --- a/tests/projections/test_projection_specifications.py +++ b/tests/projections/test_projection_specifications.py @@ -355,7 +355,7 @@ def test_formats_for_control_specification_for_mechanism_and_function_params(sel 'ControlProjection for R-CONTROL[gain]' gating_spec_list = [ - pnl.GATING, + pnl.GATE, pnl.CONTROL, pnl.GATING_SIGNAL, pnl.CONTROL_SIGNAL, @@ -371,7 +371,7 @@ def test_formats_for_control_specification_for_mechanism_and_function_params(sel pnl.ControlMechanism, pnl.GatingMechanism(), pnl.ControlMechanism(), - (0.3, pnl.GATING), + (0.3, pnl.GATE), (0.3, pnl.CONTROL), (0.3, pnl.GATING_SIGNAL), (0.3, pnl.CONTROL_SIGNAL), @@ -411,12 +411,14 @@ def test_formats_for_gating_specification_of_input_and_output_ports(self, input_ IN_NAME = G_IN[1] else: IN_NAME = G_IN - IN_CONTROL = pnl.CONTROL in repr(IN_NAME).split(".")[-1].upper() + # IN_CONTROL = pnl.CONTROL in repr(IN_NAME).split(".")[-1].upper() + IN_CONTROL = 'CONTROL' in repr(IN_NAME).split(".")[-1].upper() if isinstance(G_OUT, tuple): OUT_NAME = G_OUT[1] else: OUT_NAME = G_OUT - OUT_CONTROL = pnl.CONTROL in repr(OUT_NAME).split(".")[-1].upper() + # OUT_CONTROL = pnl.CONTROL in repr(OUT_NAME).split(".")[-1].upper() + OUT_CONTROL = 'CONTROL' in repr(OUT_NAME).split(".")[-1].upper() T = pnl.TransferMechanism( name='T-GATING', From 1470df4b5f2a0e3203d83542697e00cd473aa956 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 5 Jan 2022 01:40:21 -0500 Subject: [PATCH 082/285] llvm, functions/UDF: Add checks that we're not trying to compile closures Signed-off-by: Jan Vesely --- .../components/functions/userdefinedfunction.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index bb8b6ecc5b3..5435dc11253 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -11,7 +11,7 @@ import numpy as np import typecheck as tc -from inspect import signature, _empty, getsourcelines +from inspect import signature, _empty, getsourcelines, getclosurevars import ast from psyneulink.core.components.functions.function import FunctionError, Function_Base @@ -659,14 +659,21 @@ def _function(self, variable, context=None, **kwargs): def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, *, tags:frozenset): + # Check for global and nonlocal vars. we can't compile those. + closure_vars = getclosurevars(self.custom_function) + assert len(closure_vars.nonlocals) == 0, "Compiling functions with non-local variables is not supported!" + srclines = getsourcelines(self.custom_function)[0] - # strip preceeding space characters + # strip preceding 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_globals = closure_vars.globals + assert len(func_globals) == 0 or ( + len(func_globals) == 1 and np in func_globals.values()), \ + "Compiling functions with global variables is not supported! ({})".format(closure_vars.globals) func_params = {param_id: pnlvm.helpers.get_param_ptr(builder, self, params, param_id) for param_id in self.llvm_param_ids} udf_block = builder.append_basic_block(name="post_udf") From a1af6f24a80813fc41e32b97f940dc21509bc7f3 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 5 Jan 2022 02:28:50 -0500 Subject: [PATCH 083/285] llvm, functions/UDF: Fix basic block name for UDF body Signed-off-by: Jan Vesely --- psyneulink/core/components/functions/userdefinedfunction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index 5435dc11253..25ff0a7a9d6 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -676,7 +676,7 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, "Compiling functions with global variables is not supported! ({})".format(closure_vars.globals) func_params = {param_id: pnlvm.helpers.get_param_ptr(builder, self, params, param_id) for param_id in self.llvm_param_ids} - udf_block = builder.append_basic_block(name="post_udf") + udf_block = builder.append_basic_block(name="udf_body") udf_builder = pnlvm.ir.IRBuilder(udf_block) pnlvm.codegen.UserDefinedFunctionVisitor(ctx, builder, udf_builder, func_globals, func_params, arg_in, arg_out).visit(func_ast) From ab0e3c7443813b4ae8e1d0e6504bf9a966b80787 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 5 Jan 2022 07:10:33 -0500 Subject: [PATCH 084/285] Refactor/control specs (#2276) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • controlmechanism.py: - add controlType attribute • gatingmechanism.py: - remove Parameters.output_port._parse_output_ports (handled by ControlMechanism) * • controlmechanism.py: - add controlType attribute • gatingmechanism.py: - remove Parameters.output_port._parse_output_ports (handled by ControlMechanism) • gatingsignal.py: - remove _parse_port_specific_specs (handled by ControlSignal) * • controlmechanism.py: - add controlType attribute • gatingmechanism.py: - remove Parameters.output_port._parse_output_ports (handled by ControlMechanism) • gatingsignal.py: - remove _parse_port_specific_specs (handled by ControlSignal) * - * • controlmechanism.py: - Parameters._parse_output_port(): remove error for dual spec (handled by ControlSignal) * - * - * - * - Co-authored-by: jdcpni --- .../modulatory/control/controlmechanism.py | 30 ++------ .../control/gating/gatingmechanism.py | 56 +++----------- .../control/optimizationcontrolmechanism.py | 2 + .../ports/modulatorysignals/controlsignal.py | 75 ++++++++++--------- .../ports/modulatorysignals/gatingsignal.py | 52 +------------ psyneulink/core/compositions/composition.py | 6 ++ setup.cfg | 2 +- tests/naming/test_naming.py | 9 ++- .../test_projection_specifications.py | 11 +++ 9 files changed, 82 insertions(+), 161 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index 4e873865a34..1124589b1d7 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1029,6 +1029,7 @@ class ControlMechanism(ModulatoryMechanism_Base): """ componentType = "ControlMechanism" + controlType = CONTROL # Used as key in specification dictionaries; can be overridden by subclasses initMethod = INIT_EXECUTE_METHOD_ONLY @@ -1210,9 +1211,6 @@ class Parameters(ModulatoryMechanism_Base.Parameters): constructor_argument=CONTROL ) - # MODIFIED 1/2/22 OLD: - MUCH OF THIS SEEMS TO BE COVERED ELSEWHERE; COMMENTING OUT ONLY CAUSES PROBLEMS WITH - # test_control_signal_and_control_projection_names AND - # test_json_results_equivalence (stroop_conflict_monitoring_py) def _parse_output_ports(self, output_ports): def is_2tuple(o): return isinstance(o, tuple) and len(o) == 2 @@ -1235,27 +1233,15 @@ def is_2tuple(o): } # handle dict of form {PROJECTIONS: <2 item tuple>, : , ...} elif isinstance(output_ports[i], dict): - # Handle CONTROL as synonym of PROJECTIONS - if CONTROL in output_ports[i]: - # MODIFIED 1/3/22 NEW: - # CONTROL AND PROJECTIONS can't both be used - if PROJECTIONS in output_ports[i]: - raise ControlMechanismError(f"Both 'CONTROL' and 'PROJECTIONS' entries found in " - f"specification dict for {ControlSignal.__name__} of " - f"'{self.name}': ({output_ports[i]}).") - # MODIFIED 1/3/22 END - # Replace CONTROL with PROJECTIONS - output_ports[i][PROJECTIONS] = output_ports[i].pop(CONTROL) - if (PROJECTIONS in output_ports[i] and is_2tuple(output_ports[i][PROJECTIONS])): - full_spec_dict = { - NAME: output_ports[i][PROJECTIONS][0], - MECHANISM: output_ports[i][PROJECTIONS][1], - **{k: v for k, v in output_ports[i].items() if k != PROJECTIONS} - } - output_ports[i] = full_spec_dict + for PROJ_SPEC_KEYWORD in {PROJECTIONS, self._owner.controlType}: + if (PROJ_SPEC_KEYWORD in output_ports[i] and is_2tuple(output_ports[i][PROJ_SPEC_KEYWORD])): + tuple_spec = output_ports[i].pop(PROJ_SPEC_KEYWORD) + output_ports[i].update({ + NAME: tuple_spec[0], + MECHANISM: tuple_spec[1]}) + assert True return output_ports - # MODIFIED 1/2/22 END def _validate_input_ports(self, input_ports): if input_ports is None: diff --git a/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py index 61cea19174d..5338d545fc4 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/gating/gatingmechanism.py @@ -189,7 +189,7 @@ from psyneulink.core.globals.defaults import defaultGatingAllocation from psyneulink.core.globals.keywords import \ CONTROL, CONTROL_SIGNALS, GATE, GATING_PROJECTION, GATING_SIGNAL, GATING_SIGNALS, \ - INIT_EXECUTE_METHOD_ONLY, MONITOR_FOR_CONTROL, PORT_TYPE, PROJECTION_TYPE + INIT_EXECUTE_METHOD_ONLY, MONITOR_FOR_CONTROL, PORT_TYPE, PROJECTIONS, PROJECTION_TYPE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel @@ -373,6 +373,7 @@ class GatingMechanism(ControlMechanism): """ componentType = "GatingMechanism" + controlType = GATE initMethod = INIT_EXECUTE_METHOD_ONLY @@ -432,52 +433,6 @@ class Parameters(ControlMechanism.Parameters): constructor_argument='gate' ) - def _parse_output_ports(self, output_ports): - from psyneulink.core.globals.keywords import NAME, MECHANISM, PROJECTIONS - # # FIX: 1/2/22 - ANY WAY TO CALL SUPER TO CALL ControlMechanism.parameters.output_ports._parse_output_ports - # super().output_ports._parse_output_ports(output_ports) - - def is_2tuple(o): - return isinstance(o, tuple) and len(o) == 2 - - if not isinstance(output_ports, list): - output_ports = [output_ports] - - for i in range(len(output_ports)): - # handle 2-item tuple - if is_2tuple(output_ports[i]): - - # this is an odd case that uses two names in the name entry - # unsure what it means - if isinstance(output_ports[i][0], list): - continue - - output_ports[i] = { - NAME: output_ports[i][0], - MECHANISM: output_ports[i][1] - } - # handle dict of form {PROJECTIONS: <2 item tuple>, : , ...} - elif isinstance(output_ports[i], dict): - # Handle GATE as synonym of PROJECTIONS - if GATE in output_ports[i]: - # GATE AND PROJECTIONS can't both be used - if PROJECTIONS in output_ports[i]: - raise GatingMechanismError(f"Both 'PROJECTIONS' and 'GATE' entries found in " - f"specification dict for {GatingSignal.__name__} of " - f"'{self.name}': ({output_ports[i]}).") - # Replace GATE with PROJECTIONS - output_ports[i][PROJECTIONS] = output_ports[i].pop(GATE) - if (PROJECTIONS in output_ports[i] and is_2tuple(output_ports[i][PROJECTIONS])): - full_spec_dict = { - NAME: output_ports[i][PROJECTIONS][0], - MECHANISM: output_ports[i][PROJECTIONS][1], - **{k: v for k, v in output_ports[i].items() if k != PROJECTIONS} - } - output_ports[i] = full_spec_dict - - return output_ports - - @tc.typecheck def __init__(self, default_gating_allocation=None, @@ -543,6 +498,11 @@ def _instantiate_control_signal_type(self, gating_signal_spec, context): from psyneulink.core.components.projections.projection import ProjectionError allocation_parameter_default = self.parameters.gating_allocation.default_value + + # Handle controlType as synonym for PROJECTIONS: + if isinstance(gating_signal_spec, dict) and self.controlType in gating_signal_spec: + gating_signal_spec[PROJECTIONS] = gating_signal_spec.pop(self.controlType) + gating_signal = _instantiate_port(port_type=GatingSignal, owner=self, variable=self.default_allocation # User specified value @@ -551,8 +511,10 @@ def _instantiate_control_signal_type(self, gating_signal_spec, context): modulation=self.defaults.modulation, port_spec=gating_signal_spec, context=context) + if not type(gating_signal) in convert_to_list(self.outputPortTypes): raise ProjectionError(f'{type(gating_signal)} inappropriate for {self.name}') + return gating_signal def _check_for_duplicates(self, control_signal, control_signals, context): diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 59a0b533cbc..61587a6ed63 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1742,6 +1742,8 @@ def _create_randomization_control_signal(self, context, set_control_signal_index # FIX: 11/3/21 noise PARAM OF TransferMechanism IS MARKED AS SEED WHEN ASSIGNED A DISTRIBUTION FUNCTION, # BUT IT HAS NO PARAMETER PORT BECAUSE THAT PRESUMABLY IS FOR THE INTEGRATOR FUNCTION, # BUT THAT IS NOT FOUND BY model.all_dependent_parameters + # FIX: 1/4/22: CHECK IF THIS WORKS WITH CFA + # (i.e., DOES IT [make sense to HAVE A random_variables ATTRIBUTE?) # Get Components with variables to be randomized across estimates # and construct ControlSignal to modify their seeds over estimates if self.random_variables is ALL: diff --git a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py index 3ddd61cffbc..f9e01347b1e 100644 --- a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py @@ -595,7 +595,29 @@ class ControlSignal(ModulatorySignal): #region CLASS ATTRIBUTES componentType = CONTROL_SIGNAL + componentName = 'ControlSignal' + errorType = ControlSignalError + paramsType = OUTPUT_PORT_PARAMS + portAttributes = ModulatorySignal.portAttributes | {ALLOCATION_SAMPLES, + COST_OPTIONS, + INTENSITY_COST_FUNCTION, + ADJUSTMENT_COST_FUNCTION, + DURATION_COST_FUNCTION, + COMBINE_COSTS_FUNCTION} + + connectsWith = [PARAMETER_PORT, INPUT_PORT, OUTPUT_PORT] + connectsWithAttribute = [PARAMETER_PORTS, INPUT_PORTS, OUTPUT_PORTS] + projectionSocket = RECEIVER + modulators = [] + projection_type = CONTROL_PROJECTION + + classPreferenceLevel = PreferenceLevel.TYPE + # Any preferences specified below will override those specified in TYPE_DEFAULT_PREFERENCES + # Note: only need to specify setting; level will be assigned to TYPE automatically + # classPreferences = { + # PREFERENCE_SET_NAME: 'OutputPortCustomClassPreferences', + # PREFERENCE_KEYWORD: ...} class Parameters(ModulatorySignal.Parameters): """ @@ -768,26 +790,6 @@ def _validate_allocation_samples(self, allocation_samples): # not iterable, so assume single value pass - portAttributes = ModulatorySignal.portAttributes | {ALLOCATION_SAMPLES, - COST_OPTIONS, - INTENSITY_COST_FUNCTION, - ADJUSTMENT_COST_FUNCTION, - DURATION_COST_FUNCTION, - COMBINE_COSTS_FUNCTION} - - connectsWith = [PARAMETER_PORT, INPUT_PORT, OUTPUT_PORT] - connectsWithAttribute = [PARAMETER_PORTS, INPUT_PORTS, OUTPUT_PORTS] - projectionSocket = RECEIVER - modulators = [] - projection_type = CONTROL_PROJECTION - - classPreferenceLevel = PreferenceLevel.TYPE - # Any preferences specified below will override those specified in TYPE_DEFAULT_PREFERENCES - # Note: only need to specify setting; level will be assigned to TYPE automatically - # classPreferences = { - # PREFERENCE_SET_NAME: 'OutputPortCustomClassPreferences', - # PREFERENCE_KEYWORD: ...} - #endregion @tc.typecheck @@ -1033,37 +1035,36 @@ def _parse_port_specific_specs(self, owner, port_dict, port_specific_spec): [TBI:] (Mechanism, parameter name, weight, exponent, projection_specs) Returns params dict with CONNECTIONS entries if any of these was specified. - """ + from psyneulink.core.components.projections.projection import _parse_connection_specs params_dict = {} port_spec = port_specific_spec + dual_spec_error = self.errorType(f"Both 'PROJECTIONS' and '{owner.controlType.upper()}' entries found in " + f"specification dict for '{port_dict['port_type'].__name__}' of " + f"'{owner.name}'. Must use only one or the other.") if isinstance(port_specific_spec, dict): - # MODIFIED 1/2/22 NEW: # Note: if CONTROL is specified alone, it is moved to PROJECTIONS in Port._parse_ort_spec() - if CONTROL in port_specific_spec and PROJECTIONS in port_specific_spec: - raise ControlSignalError(f"Both 'PROJECTIONS' and 'CONTROL' entries found in specification dict " - f"for '{port_dict['port_type'].__name__}' of '{owner.name}'. " - f"Must use only one or the other.") - # MODIFIED 1/2/22 END + if owner.controlType in port_specific_spec: + # owner.controlType *and* PROJECTIONS can't both be used + if PROJECTIONS in port_specific_spec: + raise dual_spec_error + # Move owner.controlType entry to PROJECTIONS + port_specific_spec[PROJECTIONS] = port_specific_spec.pop(owner.controlType) return None, port_specific_spec elif isinstance(port_specific_spec, tuple): port_spec = None - # MODIFIED 1/2/22 NEW: - # Resolve CONTROL as synonym for PROJECTIONS: - if CONTROL in params_dict: - # CONTROL AND PROJECTIONS can't both be used + # Resolve owner.controlType as synonym for PROJECTIONS: + if owner.controlType in params_dict: + # owner.controlType *and* PROJECTIONS can't both be used if PROJECTIONS in params_dict: - raise ControlSignalError(f"Both 'PROJECTIONS' and 'CONTROL' entries found in specification dict " - f"for '{port_dict['port_type'].__name__}' of '{owner.name}'. " - f"Must use only one or the other.") - # Move CONTROL to PROJECTIONS - params_dict[PROJECTIONS] = params_dict.pop(CONTROL) - # MODIFIED 1/2/22 END + raise dual_spec_error + # Move owner.controlType entry to PROJECTIONS + params_dict[PROJECTIONS] = params_dict.pop(owner.controlType) params_dict[PROJECTIONS] = _parse_connection_specs(connectee_port_type=self, owner=owner, connections=port_specific_spec) diff --git a/psyneulink/core/components/ports/modulatorysignals/gatingsignal.py b/psyneulink/core/components/ports/modulatorysignals/gatingsignal.py index ee47bb3bf11..b24e5da5eb7 100644 --- a/psyneulink/core/components/ports/modulatorysignals/gatingsignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/gatingsignal.py @@ -347,8 +347,9 @@ class GatingSignal(ControlSignal): componentType = GATING_SIGNAL componentName = 'GatingSignal' - paramsType = OUTPUT_PORT_PARAMS + errorType = GatingSignalError + paramsType = OUTPUT_PORT_PARAMS portAttributes = ControlSignal.portAttributes | {GATE} connectsWith = [INPUT_PORT, OUTPUT_PORT] @@ -474,55 +475,6 @@ def __init__(self, transfer_function=transfer_function, **kwargs) - def _parse_port_specific_specs(self, owner, port_dict, port_specific_spec): - """Get connections specified in a ParameterPort specification tuple - - Tuple specification can be: - (Port name, Mechanism) - [TBI:] (Mechanism, Port name, weight, exponent, projection_specs) - - Returns params dict with CONNECTIONS entries if any of these was specified. - - """ - from psyneulink.core.components.projections.projection import _parse_connection_specs - - params_dict = {} - port_spec = port_specific_spec - - # MODIFIED 1/2/22 NEW: - if isinstance(port_specific_spec, dict): - # Note: if GATE is specified alone, it is moved to PROJECTIONS in Port._parse_ort_spec() - if GATE in port_specific_spec and PROJECTIONS in port_specific_spec: - raise GatingSignalError(f"Both 'PROJECTIONS' and 'GATE' entries found in specification dict " - f"for '{port_dict['port_type'].__name__}' of '{owner.name}'. " - f"Must use only one or the other.") - return None, port_specific_spec - # MODIFIED 1/2/22 END - - elif isinstance(port_specific_spec, tuple): - port_spec = None - # Resolve CONTROL as synonym for PROJECTIONS: - if GATE in params_dict: - # CONTROL AND PROJECTIONS can't both be used - if PROJECTIONS in params_dict: - raise GatingSignalError(f"Both 'PROJECTIONS' and 'GATE' entries found in specification dict " - f"for '{port_dict['port_type'].__name__}' of '{owner.name}'. " - f"Must use only one or the other.") - # Move GATE to PROJECTIONS - params_dict[PROJECTIONS] = params_dict.pop(GATE) - params_dict[PROJECTIONS] = _parse_connection_specs(connectee_port_type=self, - owner=owner, - connections=port_specific_spec) - elif port_specific_spec is not None: - raise GatingSignalError("PROGRAM ERROR: Expected tuple or dict for {}-specific params but, got: {}". - format(self.__class__.__name__, port_specific_spec)) - - if params_dict[PROJECTIONS] is None: - raise GatingSignalError("PROGRAM ERROR: No entry found in {} params dict for {} " - "with specification of {}, {} or GatingProjection(s) to it". - format(GATING_SIGNAL, INPUT_PORT, OUTPUT_PORT, owner.name)) - return port_spec, params_dict - def _instantiate_cost_functions(self, context): """Override ControlSignal as GatingSignal has not cost functions""" pass diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 4a202c0828d..b42c72c9a66 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -7718,6 +7718,7 @@ def add_controller(self, controller:ControlMechanism, context=None): for node in self.nodes: self._instantiate_deferred_init_control(node, context) + # MODIFIED 1/4/22 OLD: if hasattr(self.controller, NUM_ESTIMATES) and self.controller.num_estimates: if RANDOMIZATION_CONTROL_SIGNAL not in self.controller.output_ports.names: try: @@ -7730,6 +7731,11 @@ def add_controller(self, controller:ControlMechanism, context=None): self.controller.output_ports.names.index(RANDOMIZATION_CONTROL_SIGNAL), context ) + self.controller.function.parameters.randomization_dimension._set( + self.controller.output_ports.names.index(RANDOMIZATION_CONTROL_SIGNAL), + context + ) + # MODIFIED 1/4/22 END # ACTIVATE FOR COMPOSITION ----------------------------------------------------- diff --git a/setup.cfg b/setup.cfg index 915da5ce962..4669e2ba462 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,7 @@ markers = composition: PsyNeuLink Composition tests llvm: Tests using LLVM runtime compiler cuda: Tests using LLVM runtime compiler and CUDA GPGPU backend - control: Tests including control mechanism + control: Tests including control mechanism and/or control projection projection nested: Tests including nested compositions function: Tests of Function classes diff --git a/tests/naming/test_naming.py b/tests/naming/test_naming.py index 2082b73c47e..adb655c8ba0 100644 --- a/tests/naming/test_naming.py +++ b/tests/naming/test_naming.py @@ -154,7 +154,7 @@ def test_input_port_and_assigned_projection_names(self): # ------------------------------------------------------------------------------------------------ # TEST 11 # Test that ControlSignals and ControlProjections are properly named - + @pytest.mark.control @pytest.mark.parametrize('control_spec', [pnl.CONTROL, pnl.PROJECTIONS]) def test_control_signal_and_control_projection_names(self, control_spec): D1 = pnl.DDM(name='D1') @@ -168,9 +168,9 @@ def test_control_signal_and_control_projection_names(self, control_spec): # ControlSignal with two ControlProjection to two parameters of same Mechanism C2 = pnl.ControlMechanism(control_signals=[{control_spec:[D1.parameter_ports[ - psyneulink.core.components.functions.nonstateful.distributionfunctions.DRIFT_RATE], - D1.parameter_ports[ - psyneulink.core.globals.keywords.THRESHOLD]]}]) + psyneulink.core.components.functions.nonstateful.distributionfunctions.DRIFT_RATE], + D1.parameter_ports[ + psyneulink.core.globals.keywords.THRESHOLD]]}]) assert C2.control_signals[0].name == 'D1[drift_rate, threshold] ControlSignal' assert C2.control_signals[0].efferents[0].name == 'ControlProjection for D1[drift_rate]' assert C2.control_signals[0].efferents[1].name == 'ControlProjection for D1[threshold]' @@ -188,6 +188,7 @@ def test_control_signal_and_control_projection_names(self, control_spec): # TEST 12 # Test that GatingSignals and GatingProjections are properly named + @pytest.mark.control def test_gating_signal_and_gating_projection_names(self): T3 = pnl.TransferMechanism(name='T3') T4 = pnl.TransferMechanism(name='T4', input_ports=['First Port','Second Port']) diff --git a/tests/projections/test_projection_specifications.py b/tests/projections/test_projection_specifications.py index 583241c7aa0..eaecc1ae938 100644 --- a/tests/projections/test_projection_specifications.py +++ b/tests/projections/test_projection_specifications.py @@ -64,6 +64,7 @@ def test_projection_specification_formats(self): ('proj and mod','"Both \'modulates\' and \'projections\' arguments are specified in the constructor for ' '\'ControlSignal; Should use just \'projections\' (or \'control\') "') ]) + @pytest.mark.control def test_control_signal_projections_arg(self, args): M = pnl.ProcessingMechanism() control_specs = {pnl.CONTROL: {'control':(pnl.SLOPE, M)}, @@ -97,6 +98,7 @@ def test_control_signal_projections_arg(self, args): ('proj and mod','"Both \'modulates\' and \'projections\' arguments are specified in the constructor for ' '\'GatingSignal; Should use just \'projections\' (or \'gate\') "') ]) + @pytest.mark.control def test_gating_signal_projections_arg(self, args): M = pnl.ProcessingMechanism() gating_specs = {pnl.GATE: {'gate':(pnl.SLOPE, M)}, @@ -126,6 +128,7 @@ def test_gating_signal_projections_arg(self, args): [pnl.CONTROL, pnl.GATE, pnl.PROJECTIONS] ] ) + @pytest.mark.control def test_multiple_modulatory_projection_specs(self, control_spec, gating_spec, extra_spec): M = pnl.DDM(name='MY DDM') @@ -147,6 +150,7 @@ def test_multiple_modulatory_projection_specs(self, control_spec, gating_spec, e pnl.GatingMechanism(gating_signals=[gating_sig_spec]) assert gating_err_msg == str(err.value) else: + # G = pnl.GatingMechanism(gating_signals=[gating_sig_spec]) C = pnl.ControlMechanism(control_signals=[ctl_sig_spec]) G = pnl.GatingMechanism(gating_signals=[gating_sig_spec]) assert len(C.control_signals)==1 @@ -160,6 +164,7 @@ def test_multiple_modulatory_projection_specs(self, control_spec, gating_spec, e assert M.output_ports[pnl.DECISION_VARIABLE].mod_afferents[0]==G.gating_signals[0].efferents[0] assert M.output_ports[pnl.RESPONSE_TIME].mod_afferents[0]==G.gating_signals[0].efferents[1] + @pytest.mark.control def test_multiple_modulatory_projections_with_port_Name(self): M = pnl.DDM(name='MY DDM') @@ -182,6 +187,7 @@ def test_multiple_modulatory_projections_with_port_Name(self): assert M.output_ports[pnl.DECISION_VARIABLE].mod_afferents[0]==G.gating_signals[0].efferents[0] assert M.output_ports[pnl.RESPONSE_TIME].mod_afferents[0]==G.gating_signals[0].efferents[1] + @pytest.mark.control def test_multiple_modulatory_projections_with_mech_and_port_Name_specs(self): M = pnl.DDM(name='MY DDM') @@ -267,6 +273,7 @@ def test_2_item_tuple_from_control_signal_to_parameter_port(self): assert C.control_signals[0].efferents[0].receiver.name == 'drift_rate' assert C.control_signals[0].efferents[1].receiver.name == 'threshold' + @pytest.mark.control def test_2_item_tuple_from_parameter_port_to_control_signals(self): C = pnl.ControlMechanism(control_signals=['a','b']) @@ -279,6 +286,7 @@ def test_2_item_tuple_from_parameter_port_to_control_signals(self): assert D.parameter_ports[ psyneulink.core.globals.keywords.THRESHOLD].mod_afferents[0].sender == C.control_signals[1] + @pytest.mark.control def test_2_item_tuple_from_gating_signal_to_output_ports(self): D4 = pnl.DDM(name='D4') @@ -294,6 +302,7 @@ def test_2_item_tuple_from_gating_signal_to_output_ports(self): assert G.gating_signals[0].efferents[0].receiver.name == 'DECISION_VARIABLE' assert G.gating_signals[0].efferents[1].receiver.name == 'RESPONSE_TIME' + @pytest.mark.control def test_2_item_tuple_from_input_and_output_ports_to_gating_signals(self): G = pnl.GatingMechanism(gating_signals=['a','b']) @@ -331,6 +340,7 @@ def test_2_item_tuple_from_input_and_output_ports_to_gating_signals(self): 'noise, gain', [(noise, gain) for noise, gain in [j for j in zip(control_spec_list, reversed(control_spec_list))]] ) + @pytest.mark.control def test_formats_for_control_specification_for_mechanism_and_function_params(self, noise, gain): # This shenanigans is to avoid assigning the same instantiated ControlProjection more than once if noise == 'CP_OBJECT': @@ -394,6 +404,7 @@ def test_formats_for_control_specification_for_mechanism_and_function_params(sel 'input_port, output_port', [(inp, outp) for inp, outp in [j for j in zip(gating_spec_list, reversed(gating_spec_list))]] ) + @pytest.mark.control def test_formats_for_gating_specification_of_input_and_output_ports(self, input_port, output_port): G_IN, G_OUT = input_port, output_port From 688f1cece933e73a01671c90ea6eba02e3558244 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jan 2022 23:11:21 +0000 Subject: [PATCH 085/285] requirements: update pillow requirement from <8.5.0 to <9.1.0 (#2274) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 50b561f313d..b17b7ef1e76 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ llvmlite<0.38 matplotlib<3.4.4 networkx<2.6 numpy<1.21.4, >=1.17.0 -pillow<8.5.0 +pillow<9.1.0 pint<0.18 toposort<1.8 torch>=1.8.0, <2.0.0; (platform_machine == 'AMD64' or platform_machine == 'x86_64') and platform_python_implementation == 'CPython' and implementation_name == 'cpython' From da4ecb711214a2ec1116a885c6aa375126642e1c Mon Sep 17 00:00:00 2001 From: jdcpni Date: Thu, 6 Jan 2022 17:03:32 -0500 Subject: [PATCH 086/285] Fix/comp/build predicted input dicts (#2277) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • component.py docstring mod to **size** * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • composition.py: - build_predicted_inputs_dict(): fixed bug in handling inputs for doubly-nested comps • test_control.py: - test_nested_composition_as_agent_rep: - updated to handle test of above (and associated errors) without a nested agent_rep - test of nested agent_rep commented out until that is supported * - Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- .../compositioninterfacemechanism.py | 32 ++++++++++-- psyneulink/core/compositions/composition.py | 50 +++++++++++++------ tests/composition/test_control.py | 36 +++++++++++-- 3 files changed, 95 insertions(+), 23 deletions(-) diff --git a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py index 0c378bfced0..82f186b0969 100644 --- a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py +++ b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py @@ -232,11 +232,35 @@ def remove_ports(self, ports, context=None): output_ports_marked_for_deletion.add(port) self.user_added_ports[OUTPUT_PORTS] = self.user_added_ports[OUTPUT_PORTS] - output_ports_marked_for_deletion + def _get_source_node_for_input_CIM(self, port, start_comp=None, end_comp=None): + """Return Port, Node and Composition for source of projection to input_CIM from (possibly nested) outer comp + **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; + **comp** specifies the Composition at which to begin the search (or continue it when called recursively; + assumes the current CompositionInterfaceMechanism's Composition by default + """ + # Ensure method is being called on an output_CIM + assert self == self.composition.input_CIM + # CIM MAP ENTRIES: [SENDER PORT, [output_CIM InputPort, output_CIM OutputPort]] + # Get sender to input_port of output_CIM + comp = start_comp or self.composition + port_map = port.owner.port_map + idx = 0 if isinstance(port, InputPort) else 1 + input_port = [port_map[k][0] for k in port_map if port_map[k][idx] is port] + assert len(input_port)==1, f"PROGRAM ERROR: Expected exactly 1 input_port for {port.name} " \ + f"in port_map for {port.owner}; found {len(input_port)}." + # assert len(input_port[0].path_afferents)==1, f"PROGRAM ERROR: Port ({input_port.name}) expected to have " \ + # f"just one path_afferent; has {len(input_port.path_afferents)}." + if not input_port[0].path_afferents or comp == end_comp: + return input_port[0], input_port[0].owner, comp + sender = input_port[0].path_afferents[0].sender + # if not isinstance(sender.owner, CompositionInterfaceMechanism): + return self._get_source_node_for_input_CIM(sender, sender.owner.composition) + def _get_destination_node_for_input_CIM(self, port, comp=None): """Return Port, Node and Composition for destination of projection from input_CIM to (possibly nested) node **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; - **comp** specifies the Composition at which to begin the search; assumes the current - CompositionInterfaceMechanism's Composition by default + **comp** specifies the Composition at which to begin the search (or continue it when called recursively; + assumes the current CompositionInterfaceMechanism's Composition by default """ # Ensure method is being called on an input_CIM assert self == self.composition.input_CIM @@ -258,8 +282,8 @@ def _get_destination_node_for_input_CIM(self, port, comp=None): def _get_source_node_for_output_CIM(self, port, comp=None): """Return Port, Node and Composition for source of projection to output_CIM from (possibly nested) node **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; - **comp** specifies the Composition at which to begin the search; assumes the current - CompositionInterfaceMechanism's Composition by default + **comp** specifies the Composition at which to begin the search (or continue it when called recursively; + assumes the current CompositionInterfaceMechanism's Composition by default """ # Ensure method is being called on an output_CIM assert self == self.composition.output_CIM diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index b42c72c9a66..9a2a4654dbf 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -8008,10 +8008,11 @@ def _check_nodes_initialization_status(self, context=None): # FIX: 11/3/21 ??GET RID OF THIS AND CALL TO IT ONCE PROJECTIONS HAVE BEEN IMPLEMENTED FOR SHADOWED INPUTS # CHECK WHETHER state_input_ports ADD TO OR REPLACE shadowed_inputs def _build_predicted_inputs_dict(self, predicted_input): - """Get inputs for evaluate method used to execute simulations of Composition. + """Format predict_inputs from controller as input to evaluate method used to execute simulations of Composition. - Get values of state_input_ports which receive projections from items providing relevant input (and any - processing of those values specified + Get values of state_input_ports that receive projections from items providing relevant input (and any + processing of those values specified), and format as input_dict suitable for run() method (called in evaluate) + Deal with inputs for nodes in nested Compositions """ inputs = {} no_predicted_input = (predicted_input is None or not len(predicted_input)) @@ -8034,16 +8035,38 @@ def _build_predicted_inputs_dict(self, predicted_input): and shadow_input_owner not in nested_nodes \ and shadow_input_owner not in self.nodes: continue - if shadow_input_owner not in nested_nodes: + if shadow_input_owner in nested_nodes: + comp = nested_nodes[shadow_input_owner] + if comp in inputs: + inputs[comp]=np.concatenate([[shadowed_input],inputs[comp][0]]) + else: + def _get_enclosing_comp_for_node(input_port, comp): + """Get the Composition that is a node of self in which node nested in it. + - input_port is of node for which enclosing comp is being sought + - comp is one to which that node belongs + """ + # Get input_port for comp's CIM corresponding to + cim_input_port = comp.input_CIM.port_map[input_port][0] + # Outermost Composition, so return that + if not cim_input_port.path_afferents: + return comp + enclosing_comp = cim_input_port.path_afferents[0].sender.owner.composition + # Recursively search up through enclosing Compositions until one is a node of self + while comp not in self.nodes and comp is not self: + comp = _get_enclosing_comp_for_node(cim_input_port, enclosing_comp) + return comp + shadow_input_comp = nested_nodes[shadow_input_owner].input_CIM.composition + comp_for_input = _get_enclosing_comp_for_node(input_port.shadow_inputs, shadow_input_comp) + if comp_for_input in inputs: + # If node for nested comp is already in inputs dict, append to its input + inputs[comp_for_input][0].append(shadowed_input) + else: + # Create entry in inputs dict for nested comp containing shadowed_input + inputs[comp_for_input]=[[shadowed_input]] + else: if isinstance(shadow_input_owner, CompositionInterfaceMechanism): shadow_input_owner = shadow_input_owner.composition inputs[shadow_input_owner] = shadowed_input - else: - comp = nested_nodes[shadow_input_owner] - if comp not in inputs: - inputs[comp]=[[shadowed_input]] - else: - inputs[comp]=np.concatenate([[shadowed_input],inputs[comp][0]]) return inputs def _get_total_cost_of_control_allocation(self, control_allocation, context, runtime_params): @@ -8137,9 +8160,6 @@ def evaluate( an array with the results of each run is also returned. """ - # Apply candidate control to signal(s) for the upcoming simulation and determine its cost - total_cost = self._get_total_cost_of_control_allocation(control_allocation, context, runtime_params) - # Build input dictionary for simulation input_spec = self.parameters.input_specification.get(context) if input_spec and block_simulate and not isgenerator(input_spec): @@ -8155,8 +8175,10 @@ def evaluate( f"supplied input spec is a generator. Generators can not be used as inputs for block " f"simulation. This evaluation will not use block simulation.") + # Apply candidate control to signal(s) for the upcoming simulation and determine its cost + total_cost = self._get_total_cost_of_control_allocation(control_allocation, context, runtime_params) - # Set up aniimation for simulation + # Set up animation for simulation # HACK: _animate attribute is set in execute method, but Evaluate can be called on a Composition that has not # yet called the execute method, so we need to do a check here too. # -DTS diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index dc3713a3883..9cb07e72c4b 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -2690,7 +2690,16 @@ def test_input_CIM_assignment(self, comp_mode): @pytest.mark.control @pytest.mark.composition - def test_nested_composition_as_agent_rep(self): + @pytest.mark.parametrize('nested_agent_rep', [ + 'unnested', + # 'nested' # FIX: RESTORE once nested Composition is supported for agent_rep + ]) + @pytest.mark.parametrize('bad_state_featues', [ + 'good_state_feat', + 'bad_state_feat' + ]) + def test_nested_composition_as_agent_rep(self, nested_agent_rep, bad_state_featues): + from psyneulink.core.compositions.composition import RunError I = pnl.ProcessingMechanism(name='I') icomp = pnl.Composition(nodes=I, name='INNER COMP') @@ -2699,19 +2708,36 @@ def test_nested_composition_as_agent_rep(self): C = pnl.ProcessingMechanism(name='C') mcomp = pnl.Composition(pathways=[[A,B,C], icomp], name='MIDDLE COMP') + agent_rep = mcomp if nested_agent_rep is 'nested' else None + state_features = I.input_port if bad_state_featues is 'bad_state_feat' else None ocomp = pnl.Composition(nodes=[mcomp], name='OUTER COMP') ocm = pnl.OptimizationControlMechanism(name='OCM', - agent_rep=mcomp, # Nested Composition as agent_rep - state_features=I.input_port, + agent_rep=agent_rep, # Nested Composition as agent_rep + state_features=state_features, objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), allow_probes=True, function=pnl.GridSearch(), control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I), allocation_samples=[10, 20, 30])) ocomp.add_controller(ocm) - # FIX: CRASHES IN composition._get_total_cost_of_control_allocation() - # ocomp.run() + error_text = 'Input stimulus ([array([0.])]) for MIDDLE COMP is incompatible with its ' \ + 'external_input_values ([array([0.]), array([0.])]).' + if nested_agent_rep == 'unnested': + if bad_state_featues == 'bad_state_feat': + with pytest.raises(RunError) as error: + ocomp.run() + assert error_text in str(error.value) + else: + ocomp.run() + # FIX: CRASHES IN composition._get_total_cost_of_control_allocation() + # RESTORE 'nested' for nested_agent_rep arg (in params)once nested Composition is supported for agent_rep + else: + if bad_state_featues == 'bad_state_feat': + with pytest.raises(RunError) as error: + ocomp.run() + assert error_text in str(error.value) + ocomp.run() class TestSampleIterator: From 544c1ec40088ed06aebdd680fd804087cf712978 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Jan 2022 01:53:33 +0000 Subject: [PATCH 087/285] requirements: update sphinx-autodoc-typehints requirement (#2278) --- doc_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_requirements.txt b/doc_requirements.txt index 1e90f009848..88964085f9a 100644 --- a/doc_requirements.txt +++ b/doc_requirements.txt @@ -1,3 +1,3 @@ psyneulink-sphinx-theme<1.2.3.1 sphinx<4.2.1 -sphinx_autodoc_typehints<1.13.0 +sphinx_autodoc_typehints<1.15.0 From 21e685cb3db3219751435fd2a6cd647863b0fde5 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sat, 8 Jan 2022 11:34:43 -0500 Subject: [PATCH 088/285] Fix/show graph/cim (#2280) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * - * • showgraph.py: - _assign_incoming_edges(): allow input_CIM of outermost comp to render as node * • showgraph.py: - _assign_incoming_edges(): allow input_CIM of outermost comp to render as node - _assign_controller_components(): allow direct projections from nested nodes to OCM or ObjectiveMechanism when show_cim=False * • test_show_graph.py: add test_projections_from_nested_comp_to_ocm_or_obj_mech * - Co-authored-by: jdcpni --- .../compositioninterfacemechanism.py | 49 ++++----- psyneulink/core/compositions/showgraph.py | 58 ++++++++-- tests/composition/test_show_graph.py | 100 ++++++++++++++++++ 3 files changed, 172 insertions(+), 35 deletions(-) diff --git a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py index 82f186b0969..f7d7dabeaea 100644 --- a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py +++ b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py @@ -232,29 +232,29 @@ def remove_ports(self, ports, context=None): output_ports_marked_for_deletion.add(port) self.user_added_ports[OUTPUT_PORTS] = self.user_added_ports[OUTPUT_PORTS] - output_ports_marked_for_deletion - def _get_source_node_for_input_CIM(self, port, start_comp=None, end_comp=None): - """Return Port, Node and Composition for source of projection to input_CIM from (possibly nested) outer comp - **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; - **comp** specifies the Composition at which to begin the search (or continue it when called recursively; - assumes the current CompositionInterfaceMechanism's Composition by default - """ - # Ensure method is being called on an output_CIM - assert self == self.composition.input_CIM - # CIM MAP ENTRIES: [SENDER PORT, [output_CIM InputPort, output_CIM OutputPort]] - # Get sender to input_port of output_CIM - comp = start_comp or self.composition - port_map = port.owner.port_map - idx = 0 if isinstance(port, InputPort) else 1 - input_port = [port_map[k][0] for k in port_map if port_map[k][idx] is port] - assert len(input_port)==1, f"PROGRAM ERROR: Expected exactly 1 input_port for {port.name} " \ - f"in port_map for {port.owner}; found {len(input_port)}." - # assert len(input_port[0].path_afferents)==1, f"PROGRAM ERROR: Port ({input_port.name}) expected to have " \ - # f"just one path_afferent; has {len(input_port.path_afferents)}." - if not input_port[0].path_afferents or comp == end_comp: - return input_port[0], input_port[0].owner, comp - sender = input_port[0].path_afferents[0].sender - # if not isinstance(sender.owner, CompositionInterfaceMechanism): - return self._get_source_node_for_input_CIM(sender, sender.owner.composition) + # def _get_source_node_for_input_CIM(self, port, start_comp=None, end_comp=None): + # """Return Port, Node and Composition for source of projection to input_CIM from (possibly nested) outer comp + # **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; + # **comp** specifies the Composition at which to begin the search (or continue it when called recursively; + # assumes the current CompositionInterfaceMechanism's Composition by default + # """ + # # Ensure method is being called on an output_CIM + # assert self == self.composition.input_CIM + # # CIM MAP ENTRIES: [SENDER PORT, [output_CIM InputPort, output_CIM OutputPort]] + # # Get sender to input_port of output_CIM + # comp = start_comp or self.composition + # port_map = port.owner.port_map + # idx = 0 if isinstance(port, InputPort) else 1 + # input_port = [port_map[k][0] for k in port_map if port_map[k][idx] is port] + # assert len(input_port)==1, f"PROGRAM ERROR: Expected exactly 1 input_port for {port.name} " \ + # f"in port_map for {port.owner}; found {len(input_port)}." + # # assert len(input_port[0].path_afferents)==1, f"PROGRAM ERROR: Port ({input_port.name}) expected to have " \ + # # f"just one path_afferent; has {len(input_port.path_afferents)}." + # if not input_port[0].path_afferents or comp == end_comp: + # return input_port[0], input_port[0].owner, comp + # sender = input_port[0].path_afferents[0].sender + # # if not isinstance(sender.owner, CompositionInterfaceMechanism): + # return self._get_source_node_for_input_CIM(sender, sender.owner.composition) def _get_destination_node_for_input_CIM(self, port, comp=None): """Return Port, Node and Composition for destination of projection from input_CIM to (possibly nested) node @@ -286,7 +286,8 @@ def _get_source_node_for_output_CIM(self, port, comp=None): assumes the current CompositionInterfaceMechanism's Composition by default """ # Ensure method is being called on an output_CIM - assert self == self.composition.output_CIM + assert self == self.composition.output_CIM, f"_get_source_node_for_output_CIM called on {self.name} " \ + f"which is not an output_CIM" # CIM MAP ENTRIES: [SENDER PORT, [output_CIM InputPort, output_CIM OutputPort]] # Get sender to input_port of output_CIM comp = comp or self.composition diff --git a/psyneulink/core/compositions/showgraph.py b/psyneulink/core/compositions/showgraph.py index ed34d3773f8..c6a4f5e8374 100644 --- a/psyneulink/core/compositions/showgraph.py +++ b/psyneulink/core/compositions/showgraph.py @@ -1842,6 +1842,14 @@ def find_rcvr_comp(r, c, l): # incoming edges (from monitored mechs to objective mechanism) for input_port in objmech.input_ports: for projection in input_port.path_afferents: + # MODIFIED 1/6/22 NEW: + # Get nested source node for direct projection to objective mechanism + if isinstance(projection.sender.owner, CompositionInterfaceMechanism) and not show_cim: + cim_output_port = projection.sender + proj_sndr, node, comp = cim_output_port.owner._get_source_node_for_output_CIM(cim_output_port) + else: + proj_sndr = projection.sender + # MODIFIED 1/6/22 END if objmech in active_items: if self.active_color == BOLD: proj_color = self.controller_color @@ -1854,12 +1862,15 @@ def find_rcvr_comp(r, c, l): proj_width = str(self.default_width) if show_node_structure: sndr_proj_label = self._get_graph_node_label(composition, - projection.sender.owner, + proj_sndr.owner, show_types, show_dimensions) - if projection.sender.owner not in composition.nodes: + if (proj_sndr.owner not in composition.nodes + # MODIFIED 1/6/22 NEW: + and isinstance(proj_sndr.owner, CompositionInterfaceMechanism)): + # MODIFIED 1/6/22 END num_nesting_levels = self.num_nesting_levels or 0 - nested_comp = projection.sender.owner.composition + nested_comp = proj_sndr.owner.composition try: nesting_depth = next((k for k, v in comp_hierarchy.items() if v == nested_comp)) sender_visible = nesting_depth <= num_nesting_levels @@ -1868,11 +1879,11 @@ def find_rcvr_comp(r, c, l): else: sender_visible = True if sender_visible: - sndr_proj_label += ':' + objmech._get_port_name(projection.sender) + sndr_proj_label += ':' + objmech._get_port_name(proj_sndr) objmech_proj_label = objmech_label + ':' + objmech._get_port_name(input_port) else: sndr_proj_label = self._get_graph_node_label(composition, - projection.sender.owner, + proj_sndr.owner, show_types, show_dimensions) objmech_proj_label = self._get_graph_node_label(composition, @@ -1891,7 +1902,12 @@ def find_rcvr_comp(r, c, l): # incoming edges (from monitored mechs directly to controller) for outcome_input_port in controller.outcome_input_ports: for projection in outcome_input_port.path_afferents: - if show_node_structure: + # MODIFIED 1/6/22 NEW: + # Handled by _assign_cim_components() + if isinstance(projection.sender.owner, CompositionInterfaceMechanism) and not show_cim: + continue + # MODIFIED 1/6/22 END + if show_node_structure and show_cim: sndr_proj_label = self._get_graph_node_label(composition, projection.sender.owner, show_types, @@ -1953,12 +1969,19 @@ def find_rcvr_comp(r, c, l): g.edge(agent_rep_label, ctlr_label, color=agent_rep_color, penwidth=agent_rep_width) g.edge(ctlr_label, agent_rep_label, color=agent_rep_color, penwidth=agent_rep_width) - # get any other incoming edges to controller (i.e., other than from ObjectiveMechanism) + # get any other incoming edges to controller + # (i.e., other than from ObjectiveMechanism or directly monitored nodes) senders = set() # FIX: 11/3/21 - NEED TO MODIFY ONCE OUTCOME InputPorts ARE MOVED for i in controller.input_ports[controller.num_outcome_input_ports:]: for p in i.path_afferents: - senders.add(p.sender.owner) + # MODIFIED 1/6/22 NEW: + sender = p.sender.owner + if isinstance(sender, CompositionInterfaceMechanism) and not show_cim: + pass # FIX: 1/6/22 - PLACEMARKER FOR RELABELING INPUT_CIM AS SHADOWING INPUT OF SHADOWED NODE + assert True + # MODIFIED 1/6/22 END + senders.add(sender) self._assign_incoming_edges(g, controller, ctlr_label, @@ -2189,7 +2212,7 @@ def _assign_incoming_edges(self, and isinstance(proj.sender.owner, CompositionInterfaceMechanism) and proj.sender.owner in {composition.input_CIM, composition.parameter_CIM})]) senders.update(cims) - # HACK: FIX 6/13/20 - ADD USER-SPECIFIED TARGET NODE FOR INNER COMOSITION (NOT IN processing_graph) + # HACK: FIX 6/13/20 - ADD USER-SPECIFIED TARGET NODE FOR INNER COMPOSITION (NOT IN processing_graph) def assign_sender_edge(sndr:Union[Mechanism, Composition], proj_color:str, proj_arrowhead:str @@ -2360,7 +2383,7 @@ def assign_sender_edge(sndr:Union[Mechanism, Composition], if not sender.afferents and rcvr is not composition.controller: continue # FIX: LOOP HERE OVER sndr_spec IF THERE ARE SEVERAL - # Get node(s) from enclosing Comopsition that is/are source(s) of sender(s) + # Get node(s) from enclosing Composition that is/are source(s) of sender(s) sndrs_specs = self._trace_senders_for_original_sender_mechanism(proj, nesting_level) if not sndrs_specs: continue @@ -2371,11 +2394,15 @@ def assign_sender_edge(sndr:Union[Mechanism, Composition], enclosing_comp = comp_hierarchy[sndr_nesting_level] enclosing_g = enclosing_comp._show_graph.G # Skip: - # - cims as sources (handled in _assign_cim_componoents) + # - cims as sources (handled in _assign_cim_components) + # unless it is the input_CIM for the outermost Composition and show_cim is not true # - controller (handled in _assign_controller_components) if (isinstance(sndr, CompositionInterfaceMechanism) and rcvr is not enclosing_comp.controller and rcvr is not composition.controller + # MODIFIED 1/6/22 NEW: + and not sndr.afferents and show_cim + # MODIFIED 1/6/22 END or self._is_composition_controller(sndr, enclosing_comp)): continue if sender is composition.parameter_CIM: @@ -2563,7 +2590,12 @@ def _trace_senders_for_original_sender_mechanism(self, nesting_level -= 1 num_afferents = len(owner.port_map[proj.receiver][0].path_afferents) if num_afferents == 0: + # MODIFIED 1/6/22 OLD: return None + # # MODIFIED 1/6/22 NEW: + # # Presumably outermost Composition, so return CIM itself + # return [(owner, sender, nesting_level)] + # MODIFIED 1/6/22 END # # FIX: ITERATE OVER ALL AFFERENTS TO relevant InputPort of cim: # # MODIFIED 4/5/21 OLD: # outer_proj = owner.port_map[proj.receiver][0].path_afferents[0] @@ -2576,6 +2608,10 @@ def _trace_senders_for_original_sender_mechanism(self, sndrs = enclosing_showgraph._trace_senders_for_original_sender_mechanism(outer_proj, nesting_level) if sndrs is not None: senders.extend(sndrs) + # MODIFIED 1/6/22 NEW: + else: + senders.append((outer_proj.sender.owner, sender, nesting_level)) + # MODIFIED 1/6/22 END return senders # MODIFIED 4/5/21 END # FIX: RECEIVERS OF THIS RETURN NEED TO HANDLE LIST diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index d98ae465d06..d92c6bf1d83 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -668,6 +668,106 @@ def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer gv = ocomp.show_graph(output_fmt='source', **show_graph_kwargs) assert gv.strip() == expected_output + + # each item corresponds to the same item in _nested_show_graph_kwargs above + _nested_comp_to_ocm_or_obj_mech = [ + # # # monitor_for_control: + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tib -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + # THE FOLLOWING IS INCORRECT (SEE COMMENTS IN TEST) + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + # obj_mech + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\tib -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\tib -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\tib -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + # THE FOLLOWING IS INCORRECT (SEE COMMENTS IN TEST) + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + ] + num_show_graph_combos = len(_nested_show_graph_kwargs) + obj_mech = ['monitor_for_control'] * num_show_graph_combos + ['obj_mech'] * num_show_graph_combos + show_graph_args = list(zip(_nested_show_graph_kwargs * 2, _nested_comp_to_ocm_or_obj_mech)) + test_args = [] + ids = [] + for i in range(len(_nested_comp_to_ocm_or_obj_mech)): + show_graph_arg, expected_output = show_graph_args[i] + obj_mech_arg = obj_mech[i] + test_args.append((show_graph_arg, expected_output, obj_mech_arg)) + ids.append(obj_mech_arg + '-' + str(show_graph_arg)) + @pytest.mark.parametrize( + 'show_graph_kwargs, expected_output, obj_mech', test_args, + # ids=[str(x) for x in _nested_show_graph_kwargs]*2 + ids=[str(x) for x in ids] + ) + def test_projections_from_nested_comp_to_ocm_or_obj_mech(self, show_graph_kwargs, expected_output, obj_mech): + ia = ProcessingMechanism(name='ia') + ib = ProcessingMechanism(name='ib') + ic = ProcessingMechanism(name='ic') + id = ProcessingMechanism(name='id') + icomp = Composition(pathways=[ia,ib,ic,id], name='INNER COMP') + ocomp = Composition(nodes=[icomp], name='OUTER COMP') + + # # FIX: THIS IS SKIPPED DUE TO BUG ( + # # ??SAME BUG AS IN MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c ??) + # if ('show_node_structure' in show_graph_kwargs + # and not 'show_cim' in show_graph_kwargs + # and ('show_nested' in show_graph_kwargs and show_graph_kwargs['show_nested']==NESTED) + # ): + # pytest.skip("??graphviz BUG") + + if obj_mech == 'monitor_for_control': + monitor_for_control = [ic,ib] + objective_mechanism = None + else: + monitor_for_control = None + objective_mechanism = [ic,ib] + + ocm = OptimizationControlMechanism( + state_features=ia.input_port, + monitor_for_control=monitor_for_control, + objective_mechanism=objective_mechanism, + function=GridSearch(), + allow_probes=True, + control_signals=ControlSignal(modulates=(SLOPE,ia), + allocation_samples=[10, 20, 30]) + ) + ocomp.add_controller(ocm) + gv = ocomp.show_graph(output_fmt='source', **show_graph_kwargs) + + try: + assert gv.strip() == expected_output + except AssertionError: + # ??SAME BUG AS IN MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c ??) + if ('show_node_structure' in show_graph_kwargs + and 'show_cim' not in show_graph_kwargs + and ('show_nested' in show_graph_kwargs and show_graph_kwargs['show_nested']==NESTED) + ): + # The test in this condition is for incorrect show_graph output due an as yet undetermined bug + # in which the Projection from the OCM receives a projection to its control_signal output_port + # from a the input_port of a nested monitored node (ic) rather than from its output_port to the + # OCM's outcome_input_port. + # If the test fails in this condition, it could mean that the bug has been corrected. + # The bug may be the same one as in eb61303808ad2a5ba46fdd18d0e583283397915c + raise(AssertionError,"FAILURE TO REPLICATE BUGGY SHOW_GRAPH OUTPUT -- SEE COMMENT IN TEST") + else: + raise(AssertionError) + # def test_show_graph_for_nested_composition_as_agent_rep(self): # """Note: this is the same as test_control/test_nested_composition_as_agent_rep but with show_graph()""" # I = ProcessingMechanism(name='I') From 67653206266591ba493ad3509b5b2743323a7bf9 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Mon, 10 Jan 2022 07:39:28 -0500 Subject: [PATCH 089/285] Feat/ocm/state and state dict (#2281) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * - * • showgraph.py: - _assign_incoming_edges(): allow input_CIM of outermost comp to render as node * • showgraph.py: - _assign_incoming_edges(): allow input_CIM of outermost comp to render as node - _assign_controller_components(): allow direct projections from nested nodes to OCM or ObjectiveMechanism when show_cim=False * • test_show_graph.py: add test_projections_from_nested_comp_to_ocm_or_obj_mech * - * • optimizationcontrolmechanism.py - add state and state_dict properties • composition.py: - add _get_source() - add _get_destination() • compositioninterfacemechanism.py: - add _get_modulated_info_from_parameter_CIM() - _get_source_node_for_output_CIM -> _get_source_info_from_output_CIM - _get_destination_node_for_input_CIM -> _get_destination_info_from_input_CIM * - optimizationcontrolmechanism.py: docstring additions for state and state_dict * • optimizationcontrolmechanism.py: - state_dict: fix bug in which comp for state_feature using output_port was incorrect - _parse_state_feature_specs(): fix bug in which state_feature using output_port did not get assigned a Projection • * • optimizationcontrolmechanism.py: -_update_state_input_ports_for_controller: validate state_features that specify an output_port (vs. shadow) * - * • test_control.py: add test_ocm_state_input_ports_warnings_and_errors * - * • conf.py: restore autodoc_typehints = 'none' * • optimizationcontrolmechanism.py: _update_state_input_ports_for_controller: - add check of against format of inputs required by agent_rep.run * - * • composition.py: - add_controller(): fix bug in which state_features that receive from the outputport of a node in the same composition were not being activated * - * - * • optimizationcontrolmechanism.py: _update_state_input_ports_for_controller: modified warning message for use of state_features * - Co-authored-by: jdcpni --- docs/source/OptimizationControlMechanism.rst | 2 +- docs/source/conf.py | 2 + .../modulatory/control/controlmechanism.py | 20 +- .../control/optimizationcontrolmechanism.py | 286 ++++++++++++------ .../compositioninterfacemechanism.py | 58 +++- psyneulink/core/compositions/composition.py | 36 ++- psyneulink/core/compositions/showgraph.py | 7 +- tests/composition/test_control.py | 106 +++++++ 8 files changed, 405 insertions(+), 112 deletions(-) diff --git a/docs/source/OptimizationControlMechanism.rst b/docs/source/OptimizationControlMechanism.rst index 371812fb1a6..6c63e3aff0b 100644 --- a/docs/source/OptimizationControlMechanism.rst +++ b/docs/source/OptimizationControlMechanism.rst @@ -4,4 +4,4 @@ OptimizationControlMechanism .. automodule:: psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism :members: :private-members: - :exclude-members: Linear, random, Parameters, OptimizationControlMechanismError + :exclude-members: Linear, random, Parameters, OptimizationControlMechanismError, state_dict diff --git a/docs/source/conf.py b/docs/source/conf.py index db59aec563f..eb2a9596d60 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -372,6 +372,8 @@ def setup(app): #skip typehints autodoc_typehints = 'none' +# autodoc_typehints = 'signature' +# typehints_defaults = 'comma' default_role = 'any' diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index 1124589b1d7..9bbe85601ef 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1283,6 +1283,7 @@ def __init__(self, control = convert_to_list(control) or [] monitor_for_control = convert_to_list(monitor_for_control) or [] self.allow_probes = allow_probes + self._sim_counts = {} # For backward compatibility: if kwargs: @@ -1322,8 +1323,6 @@ def __init__(self, function = function or DefaultAllocationFunction - self._sim_counts = {} - super(ControlMechanism, self).__init__( default_variable=default_variable, size=size, @@ -1994,6 +1993,23 @@ def _activate_projections_for_compositions(self, composition=None): for proj in deeply_nested_aux_components.values(): composition.add_projection(proj, sender=proj.sender, receiver=proj.receiver) + # Add any remaining afferent Projections that have been assigned and are from nodes in composition + remaining_projections = set(self.projections) - dependent_projections - set(self.composition.projections) + for proj in remaining_projections: + # Projection is afferent: + if proj in self.afferents: + # Confirm sender is in composition + port, node, comp = composition._get_source(proj) + elif proj in self.efferents: + # Confirm receiver is in composition + port, node, comp = composition._get_destination(proj) + else: + assert False, f"PROGRAM ERROR: Attempt to activate Projection ('{proj.name}') in '{composition.name}'" \ + f" associated with its controller '{self.name}' that is neither an afferent nor " \ + f"efferent of '{self.name}' -- May be as yet unaccounted for condition." + if node in composition._get_all_nodes(): + proj._activate_for_compositions(composition) + def _apply_control_allocation(self, control_allocation, runtime_params, context): """Update values to `control_signals ` based on specified `control_allocation ` diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 61587a6ed63..16a77384c04 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -272,12 +272,12 @@ done automatically (see warning below). .. warning:: - The **state_features** specified when the `agent_rep ` is a - `CompositionFunctionApproximator` must align with the arguments of its `evaluate - ` method. Since the latter cannot always be determined automatically, - the `state_input_ports ` cannot be created automatically, nor - can the **state_features** specification be validated; thus, specifying inappropriate **state_features** may - produce errors that are unexpected or difficult to interpret. + The **state_features** specified when the `agent_rep ` + is a `CompositionFunctionApproximator` must align with the arguments of its `evaluate + ` method. Since the latter cannot always be determined + automatically, the `state_input_ports ` cannot be created + automatically, nor can the **state_features** specification be validated; thus, specifying inappropriate + **state_features** may produce errors that are unexpected or difficult to interpret. COMMENT: FIX: CONFIRM (OR IMPLEMENT?) THE FOLLOWING @@ -290,6 +290,7 @@ The specifications in the **state_features** argument are used to construct the `state_input_ports `, and can be any of the following, used either singly or in a list: + .. _Optimization_Control_Mechanism_Input_Port_State_Feature: * *InputPort specification* -- this creates an `InputPort` as one of the OptimizationControlMechanism's `state_input_ports ` that `shadows ` the input to the specified InputPort; that is, the value of which is used as the corresponding value of the @@ -313,17 +314,36 @@ .. technical_note:: The InputPorts specified as state_features are marked as `internal_only ` = `True`. + .. _Optimization_Control_Mechanism_Output_Port_State_Feature: * *OutputPort specification* -- this can be any form of `OutputPort specification ` for any `OutputPort` of another `Mechanism ` in the Composition; the `value ` of the specified OutputPort is used as the corresponding value of the OptimizationControlMechanism's `state_feature_values `. - * *Mechanism* -- if the `agent_rep ` is a Composition, it must be an - `INPUT ` `Node ` of that Composition, and the Mechanism's `primary InputPort - ` is used (since in this case the state_feature must correspond to an input to the Composition). - If the `agent_rep ` is a `CompositionFunctionApproximator`, then the - Mechanism's `primary OutputPort ` is used (since is the typical usage for specifying an - InputPort); if the input to the Mechanism is to be shadowed, then its InputPort must be specified explicitly. + .. _Optimization_Control_Mechanism_Mechanism_State_Feature: + * *Mechanism* -- if the `agent_rep ` is a Composition, the Mechanism must + be an `INPUT ` `Node ` of that Composition, and the Mechanism's `primary + InputPort ` is `shadowed ` (since in + this case the state_feature must correspond to an input to the Composition). If the Mechanism is not an `INPUT + ` Node, an error is generated; if its OutputPort is to be used, that needs to be specified + explicitly (as described `above `). In contrast, if the + `agent_rep ` is a `CompositionFunctionApproximator, then the Mechanism's + `primary OutputPort ` *is* used (since that is the typical usage for specifying an `InputPort + `); if the input to the Mechanism is to be shadowed, then its InputPort must be + specified explicitly (as described `above `). + + * *Mechanism* -- if the `agent_rep ` is a Composition, the Mechanism's + `primary InputPort ` is shadowed in same way as if it had been explicit `input_port + specification `. If the Mechanism is in a `nested + Composition `, it must be an `INPUT ` `Node ` of that + Composition (see note above); if its OutputPort needs to be used, it must be specified explicitly (as described + `above `). In contrast, if the `agent_rep + ` is a `CompositionFunctionApproximator`, then the Mechanism's + `primary OutputPort ` *is* used (since that is typical usage, and there are no assumptions + made about the state features of a `CompositionFunctionApproximator`); if the input to the Mechanism *is* to be + shadowed, then its InputPort must be specified explicitly (as described `above + `). + COMMENT: FIX: CONFIRM THAT THE FOLLOWING ALL WORK @@ -444,14 +464,16 @@ *State* ~~~~~~~ -The current state of the OptimizationControlMechanism -- or, more properly, its `agent_rep +The current state of the OptimizationControlMechanism -- or, more properly, of its `agent_rep ` -- is determined by the OptimizationControlMechanism's current `state_feature_values ` (see `below `) and `control_allocation `. These are provided as input to the `evaluate_agent_rep ` method, the results of which are used together with the `costs ` associated with the `control_allocation `, to evaluate the `net_outcome -` for that state. +` for that state. The current state is listed in the OptimizationControlMechanism's +`state ` attribute, and `state_dict ` +contains the Components associated with each value of `state `. .. _OptimizationControlMechanism_Input: @@ -465,7 +487,7 @@ is used to execute it; and `outcome_input_ports ` that provide the outcome of executing the `agent_rep `, that is used to compute the `net_outcome ` for the `control_allocation ` under which the -execution occurred. Each of these is described below. +execution occurred. Each of these is described below. .. _OptimizationControlMechanism_State_Features: @@ -868,7 +890,7 @@ from psyneulink.core.globals.defaults import defaultControlAllocation from psyneulink.core.globals.keywords import \ ALL, COMPOSITION, COMPOSITION_FUNCTION_APPROXIMATOR, CONCATENATE, DEFAULT_VARIABLE, EID_FROZEN, \ - FUNCTION, INTERNAL_ONLY, OPTIMIZATION_CONTROL_MECHANISM, OWNER_VALUE, PARAMS, PROJECTIONS, \ + FUNCTION, INTERNAL_ONLY, NAME, OPTIMIZATION_CONTROL_MECHANISM, OWNER_VALUE, PARAMS, PROJECTIONS, \ SHADOW_INPUTS, SHADOW_INPUT_NAME from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel @@ -907,6 +929,7 @@ def _control_allocation_search_space_getter(owning_component=None, context=None) else: return search_space + class OptimizationControlMechanism(ControlMechanism): """OptimizationControlMechanism( \ agent_rep=None, \ @@ -942,7 +965,7 @@ class OptimizationControlMechanism(ControlMechanism): ` and used to predict `net_outcome `. Any `InputPort specification ` can be used that resolves to an `OutputPort` that projects to that InputPort (see - `state_features ` for additional details>). + `state_features ` for additional details). state_feature_functions : Function or function : default None specifies the `function ` assigned the `InputPort` in `state_input_ports @@ -1068,11 +1091,21 @@ class OptimizationControlMechanism(ControlMechanism): ` in a given `OptimizationControlMechanism_State` (see `Outcome ` for additional details). - COMMENT: state : ndarray - lists the values of the current state -- a concatenation of the state_feature_values and control_allocation - following the last execution of the `agent_rep `. - COMMENT + lists the values of the current state -- a concatenation of the `state_feature_values + ` and `control_allocation + ` following the last execution of `agent_rep + `. + + state_dict : Dict[node:value] + dictionary containing information about the Components corresponding to the values in `state + `. Keys are (`Port`, `Mechanism`, `Composition`) tuples, and values are + the corresponding values in `state `. The initial entries are for the + OptimizationControlMechanism's `state features `, that are the + sources of its `state_feature_values `; they are + followed by entries for the parameters modulated by the OptimizationControlMechanism's `control_signals + ` using the corresponding values of its `control_allocations + `. num_estimates : int determines the number independent runs of `agent_rep ` (i.e., calls to @@ -1186,7 +1219,8 @@ class OptimizationControlMechanism(ControlMechanism): ` specifications for each of the OptimizationControlMechanism's `control_signals `, and includes the *RANDOMIZATION_CONTROL_SIGNAL* used to randomize estimates of each `control_allocation - ` (see `note ` above). + ` (see `note ` + above). saved_samples : list contains all values of `control_allocation ` sampled by `function @@ -1460,19 +1494,12 @@ def _validate_params(self, request_set, target_set=None, context=None): if self.random_variables is not ALL: - # invalid_params = [param.name for param in self.random_variables - # if param not in [r._owner._owner for r in self.agent_rep.random_variables]] - # if invalid_params: - # raise OptimizationControlMechanismError(f"The following Parameters were specified for the " - # f"{RANDOM_VARIABLES} arg of {self.name} that are do randomizable " - # f"(i.e., they do not have a 'seed' attribute: " - # f"{invalid_params}.") invalid_params = [param.name for param in self.random_variables if param not in self.agent_rep.random_variables] if invalid_params: raise OptimizationControlMechanismError(f"The following Parameters were specified for the " - f"{RANDOM_VARIABLES} arg of {self.name} that are do randomizable " - f"(i.e., they do not have a 'seed' attribute: " + f"{RANDOM_VARIABLES} arg of {self.name} that are do" + f"randomizable (i.e., they do not have a 'seed' attribute: " f"{invalid_params}.") # FIX: CONSIDER GETTING RID OF THIS METHOD ENTIRELY, AND LETTING state_input_ports @@ -1483,7 +1510,7 @@ def _instantiate_input_ports(self, context=None): This instantiates the OptimizationControlMechanism's `state_input_ports; these are used to provide input to the agent_rep when its evaluate method is called (see Composition._build_predicted_inputs_dict). - The OptimizationCOntrolMechanism's outcome_input_ports are instantiated by + The OptimizationControlMechanism's outcome_input_ports are instantiated by ControlMechanism._instantiate_input_ports in the call to super(). InputPorts are constructed for **state_features** by calling _parse_state_feature_specs @@ -1496,9 +1523,9 @@ def _instantiate_input_ports(self, context=None): - every outcome_input_ports receive Projections from within the agent_rep if it is a Composition. If no **state_features** are specified in the constructor, assign ones for INPUT Nodes of owner. - - warn for model-free `model-free optimization `. - - ignore here for `model-based optimization ` - (handled in _update_state_input_ports_for_controller) + - warn for use of CompositionFunctionApproximator as agent_rep; + - ignore here for Composition as agent_rep + (handled in _update_state_input_ports_for_controller). See `state_features ` and `OptimizationControlMechanism_State_Features` for additional details. @@ -1511,19 +1538,18 @@ def _instantiate_input_ports(self, context=None): # ADD CHECK IN _parse_state_feature_specs THAT IF A NODE RATHER THAN InputPort IS SPECIFIED, # ITS PRIMARY IS USED (SEE SCRATCH PAD FOR EXAMPLES) if not self.state_features: - # For model-free (agent_rep = CompositionFunctionApproximator), warn if no state_features specified. - # Note: for model-based optimization, state_input_ports and any state_feature_functions specified + # If agent_rep is CompositionFunctionApproximator, warn if no state_features specified. + # Note: if agent rep is Composition, state_input_ports and any state_feature_functions specified # are assigned in _update_state_input_ports_for_controller. if self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR: warnings.warn(f"No 'state_features' specified for use with `agent_rep' of {self.name}") else: - # FIX: 11/29/21: DISALLOW FOR COMPOSITION # Implement any specified state_features state_input_ports_specs = self._parse_state_feature_specs(self.state_features, self.state_feature_functions) # Note: - # if state_features were specified for model-free (i.e., agent_rep is a CompositionFunctionApproximator), + # if state_features were specified and agent_rep is a CompositionFunctionApproximator, # assume they are OK (no way to check their validity for agent_rep.evaluate() method, and skip assignment # Pass state_input_ports_sepcs to ControlMechanism for instantiation and addition to OCM's input_ports @@ -1552,20 +1578,22 @@ def _validate_monitor_for_control(self, nodes): super()._validate_monitor_for_control(self.agent_rep._get_all_nodes()) except ControlMechanismError as e: raise OptimizationControlMechanismError(f"{self.name} has 'outcome_ouput_ports' that receive " - f"Projections from the following Components that do not " - f"belong to its {AGENT_REP} ({self.agent_rep.name}): {e.data}.") + f"Projections from the following Components that do not belong " + f"to its {AGENT_REP} ({self.agent_rep.name}): {e.data}.") def _update_state_input_ports_for_controller(self, context=None): """Check and update state_input_ports for model-based optimization (agent_rep==Composition) - If no agent_rep has been specified or it is model-free, return - (note: validation of state_features specified for model-free optimization is up to the - CompositionFunctionApproximator) + If no agent_rep has been specified or it is a CompositionFunctionApproximator, return + (note: validation of state_features specified for CompositionFunctionApproximator optimization + is up to the CompositionFunctionApproximator) - For model-based optimization (agent_rep is a Composition): + For agent_rep that is a Composition): - - ensure that state_input_ports for all specified state_features are for InputPorts of INPUT Nodes of agent_rep; - raises an error if any receive a Projection that is not a shadow Projection from an INPUT Node of agent_rep + - ensure that state_input_ports for all specified state_features are either for InputPorts of INPUT Nodes of + agent_rep, or from Nodes of it or any nested Compositions; + raise an error if any receive a Projection that is not a shadow Projection from an INPUT Node of agent_rep + or from the output_port of a Node that is not somewhere in the agent_rep Composition. (note: there should already be state_input_ports for any **state_features** specified in the constructor). - if no state_features specified, assign a state_input_port for every InputPort of every INPUT Node of agent_rep @@ -1592,7 +1620,8 @@ def _update_state_input_ports_for_controller(self, context=None): if self.agent_rep_type != COMPOSITION: return - from psyneulink.core.compositions.composition import Composition, NodeRole, CompositionInterfaceMechanism + from psyneulink.core.compositions.composition import \ + Composition, NodeRole, CompositionInterfaceMechanism, CompositionError def _get_all_input_nodes(comp): """Return all input_nodes, including those for any Composition nested one level down. @@ -1608,15 +1637,8 @@ def _get_all_input_nodes(comp): return input_nodes if self.state_features: - # FIX: 11/26/21 - EXPLAIN THIS BEHAVIOR IN DOSCSTRING; - warnings.warn(f"The 'state_features' argument has been specified for {self.name}, that is being " - f"configured as a model-based {self.__class__.__name__} (i.e, one that uses a " - f"{Composition.componentType} as its agent_rep). This overrides automatic assignment of " - f"all inputs to its agent_rep ({self.agent_rep.name}) as the 'state_features'; only the " - f"ones specified will be used ({self.state_features}), and they must match the shape of the " - f"input to {self.agent_rep.name} when it is run. Remove this specification from the " - f"constructor for {self.name} if automatic assignment is preferred.") - + # Validate state_features, and instantiate any that are not shadowing nodes + # Shadowing nodes are instantiated in Composition._update_shadow_projections() comp = self.agent_rep # Ensure that all InputPorts shadowed by specified state_input_ports # are in agent_rep or one of its nested Compositions @@ -1628,13 +1650,23 @@ def _get_all_input_nodes(comp): comp._get_nested_compositions() if isinstance(input_port.shadow_inputs.owner, CompositionInterfaceMechanism)]))] + # Ensure any Projections received from output_ports are from Nodes in agent_rep or its nested Compositions + for input_port in self.state_input_ports: + if input_port.shadow_inputs: + continue + try: + all(comp._get_source(p) for p in input_port.path_afferents) + # except CompositionError: + except CompositionError: + invalid_state_features.append(input_port) + if any(invalid_state_features): raise OptimizationControlMechanismError(f"{self.name}, being used as controller for model-based " f"optimization of {self.agent_rep.name}, has 'state_features' " f"specified ({[d.name for d in invalid_state_features]}) that " f"are missing from the Composition or any nested within it.") - # Ensure that all InputPorts shadowed by specified state_input_ports + # Ensure that all InputPorts shadowed by specified state_input_ports # reference INPUT Nodes of agent_rep or of a nested Composition invalid_state_features = [input_port for input_port in self.state_input_ports if (input_port.shadow_inputs @@ -1651,9 +1683,30 @@ def _get_all_input_nodes(comp): f"specified ({[d.name for d in invalid_state_features]}) that " f"are not INPUT nodes for the Composition or any nested " f"within it.") + # # MODIFIED 1/9/22 NEW: + # try: + # # Test whether state_features specified are compatible with inputs format required by agent_rep + # self.agent_rep._build_predicted_inputs_dict(None) + # except: + # raise OptimizationControlMechanismError( + # f"The 'state_features' argument has been specified for '{self.name}' that is using a " + # f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but the 'state_features' " + # f"({self.state_features}) specified are not compatible with the inputs required by 'agent_rep' " + # f"when it is executed. It's get_inputs_format() method can be used to see the format required; " + # f"You can also remove the specification of 'state_features' from the constructor for {self.name} " + # f"to allow their automatic assignment.") + # MODIFIED 1/9/22 END + + warnings.warn(f"The 'state_features' argument has been specified for '{self.name}', that is being " + f"configured to use a {Composition.componentType} ('{self.agent_rep.name}') as its " + f"'{AGENT_REP}'). This overrides automatic assignment of its 'state_features' as inputs to " + f"'{self.agent_rep.name}' when it is executed. If they are not properly configured, it " + f"will cause an error. Remove this specification from the constructor for '{self.name}' to " + f"automatically configure its 'state_features' to be the external inputs to " + f"'{self.agent_rep.name}'.") return - # Model-based agent_rep, but no state_features have been specified, + # agent_rep is Composition, but no state_features have been specified, # so assign a state_input_port to shadow every InputPort of every INPUT node of agent_rep shadow_input_ports = [] for node in _get_all_input_nodes(self.agent_rep): @@ -1884,11 +1937,12 @@ def _execute(self, variable=None, context=None, runtime_params=None): # Get control_allocation that optimizes net_outcome using OptimizationControlMechanism's function # IMPLEMENTATION NOTE: skip ControlMechanism._execute since it is a stub method that returns input_values optimal_control_allocation, optimal_net_outcome, saved_samples, saved_values = \ - super(ControlMechanism,self)._execute(variable=control_allocation, - num_estimates=self.parameters.num_estimates._get(context), - context=context, - runtime_params=runtime_params - ) + super(ControlMechanism,self)._execute( + variable=control_allocation, + num_estimates=self.parameters.num_estimates._get(context), + context=context, + runtime_params=runtime_params + ) # clean up frozen values after execution self.agent_rep._delete_contexts(self._get_frozen_context(context)) @@ -2049,7 +2103,9 @@ def _gen_llvm_net_outcome_function(self, *, ctx, tags=frozenset()): data_out = builder.gep(op_in, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) if data_in.type != data_out.type: - warnings.warn("Shape mismatch: Allocation sample '{}' ({}) doesn't match input port input ({}).".format(i, self.parameters.control_allocation_search_space.get(), op.defaults.variable)) + warnings.warn(f"Shape mismatch: Allocation sample '{i}' " + f"({self.parameters.control_allocation_search_space.get()}) " + f"doesn't match input port input ({op.defaults.variable}).") assert len(data_out.type.pointee) == 1 data_out = builder.gep(data_out, [ctx.int32_ty(0), ctx.int32_ty(0)]) @@ -2076,13 +2132,10 @@ def _gen_llvm_net_outcome_function(self, *, ctx, tags=frozenset()): builder.ret_void() return llvm_func - def _gen_llvm_evaluate_alloc_range_function(self, *, ctx:pnlvm.LLVMBuilderContext, - tags=frozenset()): + def _gen_llvm_evaluate_alloc_range_function(self, *, ctx:pnlvm.LLVMBuilderContext, tags=frozenset()): assert "evaluate" in tags assert "alloc_range" in tags - evaluate_f = ctx.import_llvm_function(self, - tags=tags - {"alloc_range"}) - + evaluate_f = ctx.import_llvm_function(self, tags=tags - {"alloc_range"}) args = [*evaluate_f.type.pointee.args[:2], ctx.int32_ty, ctx.int32_ty, @@ -2116,8 +2169,7 @@ def _gen_llvm_evaluate_alloc_range_function(self, *, ctx:pnlvm.LLVMBuilderContex builder.ret_void() return llvm_func - def _gen_llvm_evaluate_function(self, *, ctx:pnlvm.LLVMBuilderContext, - tags=frozenset()): + def _gen_llvm_evaluate_function(self, *, ctx:pnlvm.LLVMBuilderContext, tags=frozenset()): assert "evaluate" in tags args = [ctx.get_param_struct_type(self.agent_rep).as_pointer(), ctx.get_state_struct_type(self.agent_rep).as_pointer(), @@ -2352,18 +2404,25 @@ def _parse_state_feature_specs(self, state_features, feature_functions, context= for spec in _state_input_ports: # MODIFIED 11/29/21 NEW: # If optimization uses Composition, assume that shadowing a Mechanism means shadowing its primary InputPort - if isinstance(spec, Mechanism) and self.agent_rep_type == COMPOSITION: - # FIX: 11/29/21: MOVE THIS TO _parse_shadow_inputs - # (ADD ARG TO THAT FOR DOING SO, OR RESTRICTING TO INPUTPORTS IN GENERAL) - if len(spec.input_ports)!=1: - raise OptimizationControlMechanismError(f"A Mechanism ({spec.name}) is specified in the " - f"'{STATE_FEATURES}' arg for {self.name} that has " - f"more than one InputPort; a specific one or subset " - f"of them must be specified.") - spec = spec.input_port - parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) # returns InputPort dict - parsed_spec[PARAMS].update({INTERNAL_ONLY:True, - PROJECTIONS:None}) + if isinstance(spec, Mechanism): + if self.agent_rep_type == COMPOSITION: + # FIX: 11/29/21: MOVE THIS TO _parse_shadow_inputs + # (ADD ARG TO THAT FOR DOING SO, OR RESTRICTING TO INPUTPORTS IN GENERAL) + if len(spec.input_ports)!=1: + raise OptimizationControlMechanismError(f"A Mechanism ({spec.name}) is specified in the " + f"'{STATE_FEATURES}' arg for {self.name} that has " + f"more than one InputPort; a specific one or subset " + f"of them must be specified.") + spec = spec.input_port + else: + spec = spec.output_port + parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) + if not parsed_spec[NAME]: + parsed_spec[NAME] = spec.full_name + if SHADOW_INPUTS in parsed_spec[PARAMS]: + # Composition._update_shadow_projections will take care of PROJECTIONS specification + parsed_spec[PARAMS].update({INTERNAL_ONLY:True, + PROJECTIONS:None}) if feature_functions: if isinstance(feature_functions, dict) and spec in feature_functions: feat_fct = feature_functions.pop(spec) @@ -2385,7 +2444,64 @@ def num_state_input_ports(self): @property def state(self): - return self.state_feature_values + self.control_allocation + state_feature_values = (self.state_feature_values if len(self.state_feature_values) + else self.state_input_ports.values) + return np.append(state_feature_values, self.control_allocation, 0) + + # FIX: 1/6/22 - FINISH IMPLEMENTING: + # - ADD ENTRIES FOR ALL NODES THAT CONTRIBUTE TO STATE_INPUT_PORTS, EVEN IF CONVERGENT + # - ADD ENTRIES FOR ALL NODES MODULATED BY CONTROL_SIGNAL EVEN IF DIVERGENT + # - DEAL WITH CONTROL_SIGNALS THAT PROJECT TO NESTED NODES (GET METHOD FROM parameter_CIM) + # - MODIFY KEYS TO BE (NODE, PORT) TUPLE + # - DOCUMENT CHANGE TO KEYS UNDER ATTRIBUTES (state_dict : ) + @property + def state_dict(self): + """Return dict with (node, port, Composition, index) tuples as keys and corresponding state[index] as values. + Note: the index is required, since a state_input_port may have more than one afferent Projection + (that is, a state_feature_value may be determined by more than one node), + and a ControlSignal may have more than one ControlProjection (that is, a given element of the + control_allocation may apply to more than one Parameter). + """ + + state_dict = {} + + # Get sources for state_feature_values of state: + for state_index, port in enumerate(self.state_input_ports): + get_info_method = self.composition._get_source + # MODIFIED 1/8/22: ONLY ONE PROJECTION PER STATE FEATURE + if port.shadow_inputs: + port = port.shadow_inputs + if port.owner in self.composition.nodes: + composition = self.composition + else: + composition = port.path_afferents[0].sender.owner.composition + get_info_method = composition._get_destination + assert port.path_afferents, f"PROGRAM ERROR: state_input_port {state_index} ('{port.name}')" \ + f"for {self.name} does not have any Projections to it" + source_port, node, comp = get_info_method(port.path_afferents[0]) + state_dict.update({(source_port, node, comp, state_index):self.state[state_index]}) + # # MODIFIED 1/8/22 ALT: SEPARATELY LISTS OUTPUT_PORTS THAT PROJECT TO SAME SHADOWED INPUT_PORT + # if port.shadow_inputs: + # port = port.shadow_inputs + # if port.owner in self.composition.nodes: + # composition = self.composition + # get_info_method = composition._get_source + # else: + # composition = port.path_afferents[0].sender.owner.composition + # get_info_method = composition._get_destination + # for projection in port.path_afferents: + # source_port, node, comp = get_info_method(projection) + # state_dict.update({(source_port, node, comp, state_index):self.state[state_index]}) + # MODIFIED 1/8/22 END + + state_index += 1 + # Get recipients of control_allocations values of state: + for ctl_index, control_signal in enumerate(self.control_signals): + for projection in control_signal.efferents: + port, node, comp = self.composition._get_destination(projection) + state_dict.update({(port, node, comp, state_index + ctl_index):self.state[state_index + ctl_index]}) + + return state_dict @property def _model_spec_parameter_blacklist(self): diff --git a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py index f7d7dabeaea..5a65c562c4a 100644 --- a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py +++ b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py @@ -256,17 +256,18 @@ def remove_ports(self, ports, context=None): # # if not isinstance(sender.owner, CompositionInterfaceMechanism): # return self._get_source_node_for_input_CIM(sender, sender.owner.composition) - def _get_destination_node_for_input_CIM(self, port, comp=None): - """Return Port, Node and Composition for destination of projection from input_CIM to (possibly nested) node - **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; - **comp** specifies the Composition at which to begin the search (or continue it when called recursively; - assumes the current CompositionInterfaceMechanism's Composition by default + def _get_destination_info_from_input_CIM(self, port, comp=None): + """Return Port, Node and Composition for "ultimate" destination of projection to **port**. + **port**: InputPort or OutputPort of the input_CIM to which the projection of interest projects; + used to find destination (key) in output_CIM's port_map. + **comp**: Composition at which to begin the search (or continue it when called recursively); + assumes the Composition for the input_CIM to which **port** belongs by default """ # Ensure method is being called on an input_CIM assert self == self.composition.input_CIM # CIM MAP ENTRIES: [RECEIVER PORT, [input_CIM InputPort, input_CIM OutputPort]] # Get receiver of output_port of input_CIM - comp = comp or self + comp = comp or self.composition port_map = port.owner.port_map idx = 0 if isinstance(port, InputPort) else 1 output_port = [port_map[k][1] for k in port_map if port_map[k][idx] is port] @@ -277,16 +278,41 @@ def _get_destination_node_for_input_CIM(self, port, comp=None): receiver = output_port[0].efferents[0].receiver if not isinstance(receiver.owner, CompositionInterfaceMechanism): return receiver, receiver.owner, comp - return self._get_destination_node_for_input_CIM(receiver, receiver.owner.composition) - - def _get_source_node_for_output_CIM(self, port, comp=None): - """Return Port, Node and Composition for source of projection to output_CIM from (possibly nested) node - **port** should be an InputPort or OutputPort of the CompositionInterfaceMechanism; - **comp** specifies the Composition at which to begin the search (or continue it when called recursively; - assumes the current CompositionInterfaceMechanism's Composition by default + return self._get_destination_info_from_input_CIM(receiver, receiver.owner.composition) + + def _get_modulated_info_from_parameter_CIM(self, port, comp=None): + """Return Port, Node and Composition for parameter modulated by ControlSignal that projects to parameter_CIM. + **port**: InputPort or OutputPort of the parameter_CIM to which the ControlSignal projects; + used to find destination (key) in parameter_CIM's port_map. + **comp**: Composition at which to begin the search (or continue it when called recursively); + assumes the Composition for the parameter_CIM to which **port** belongs by default. + """ + # Ensure method is being called on a parameter_CIM + assert self == self.composition.parameter_CIM + # CIM MAP ENTRIES: [RECEIVER PORT, [input_CIM InputPort, input_CIM OutputPort]] + # Get receiver of output_port of input_CIM + comp = comp or self.composition + port_map = port.owner.port_map + idx = 0 if isinstance(port, InputPort) else 1 + output_port = [port_map[k][1] for k in port_map if port_map[k][idx] is port] + assert len(output_port)==1, f"PROGRAM ERROR: Expected exactly 1 output_port for {port.name} " \ + f"in port_map for {port.owner}; found {len(output_port)}." + assert len(output_port[0].efferents)==1, f"PROGRAM ERROR: Port ({output_port.name}) expected to have " \ + f"just one efferent; has {len(output_port.efferents)}." + receiver = output_port[0].efferents[0].receiver + if not isinstance(receiver.owner, CompositionInterfaceMechanism): + return receiver, receiver.owner, comp + return self._get_modulated_info_from_parameter_CIM(receiver, receiver.owner.composition) + + def _get_source_info_from_output_CIM(self, port, comp=None): + """Return Port, Node and Composition for "original" source of projection from **port**. + **port** InputPort or OutputPort of the output_CIM from which the projection of interest projects; + used to find source (key) in output_CIM's port_map. + **comp** Composition at which to begin the search (or continue it when called recursively); + assumes the current CompositionInterfaceMechanism's Composition by default. """ # Ensure method is being called on an output_CIM - assert self == self.composition.output_CIM, f"_get_source_node_for_output_CIM called on {self.name} " \ + assert self == self.composition.output_CIM, f"_get_source_info_from_output_CIM called on {self.name} " \ f"which is not an output_CIM" # CIM MAP ENTRIES: [SENDER PORT, [output_CIM InputPort, output_CIM OutputPort]] # Get sender to input_port of output_CIM @@ -301,10 +327,10 @@ def _get_source_node_for_output_CIM(self, port, comp=None): sender = input_port[0].path_afferents[0].sender if not isinstance(sender.owner, CompositionInterfaceMechanism): return sender, sender.owner, comp - return self._get_source_node_for_output_CIM(sender, sender.owner.composition) + return self._get_source_info_from_output_CIM(sender, sender.owner.composition) def _sender_is_probe(self, output_port): """Return True if source of output_port is a PROBE Node of the Composition to which it belongs""" from psyneulink.core.compositions.composition import NodeRole - port, node, comp = self._get_source_node_for_output_CIM(output_port, self.composition) + port, node, comp = self._get_source_info_from_output_CIM(output_port, self.composition) return NodeRole.PROBE in comp.get_roles_by_node(node) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 9a2a4654dbf..c2616491ab7 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -5991,6 +5991,34 @@ def _check_for_nesting_with_absolute_conditions(self, scheduler, termination_con if warn: warnings.warn(warn_str) + def _get_source(self, projection): + """Return tuple with port, node and comp of sender for **projection** (possibly in a nested Composition).""" + # Note: if Projection is shadowing the input to a Node, the information returned will be for + # the output_port of the input_CIM that projects to the shadowed Node. + port = projection.sender + if port.owner in self.nodes: + return (port, port.owner, self) + elif isinstance(port.owner, CompositionInterfaceMechanism): + return port.owner._get_source_info_from_output_CIM(port) + else: + # Get info for nested node + node, comp = next((item for item in self._get_nested_nodes() if item[0] is port.owner), (None, None)) + if node: + return(port, node, comp) + else: + raise CompositionError(f"No source found for {projection.name} in {self.name}.") + + def _get_destination(self, projection): + """Return tuple with port, node and comp of receiver for **projection** (possibly in a nested Composition).""" + port = projection.receiver + if isinstance(port.owner, CompositionInterfaceMechanism): + if isinstance(projection.sender.owner, ModulatoryMechanism_Base): + return port.owner._get_modulated_info_from_parameter_CIM(port) + else: + return port.owner._get_destination_info_from_input_CIM(port) + else: + return (port, port.owner, self) + # endregion PROJECTIONS # ****************************************************************************************************************** @@ -7580,7 +7608,7 @@ def _get_deeply_nested_aux_projections(self, node): aux_projections[i] = i nested_nodes = self._get_nested_nodes() for spec, proj in aux_projections.items(): - # FIX: TREATMENT OF RECEIVERS SEEMS TO DEAL WITH ONLY RECEIVERS IN COMPS NESTED MORE THAN ON LEVEL DEEP + # FIX: TREATMENT OF RECEIVERS SEEMS TO DEAL WITH ONLY RECEIVERS IN COMPS NESTED MORE THAN ONE LEVEL DEEP # REMOVING "if not i[1] in self.nodes" crashes in test_multilevel_control if ((proj.sender.owner not in self.nodes and proj.sender.owner in [i[0] for i in nested_nodes]) @@ -8619,7 +8647,7 @@ def _parse_labels(self, inputs, mech=None, context=None): and any(n.input_labels_dict for n in k._get_nested_nodes_with_same_roles_at_all_levels(k,NodeRole.INPUT))): for i, port in enumerate(k.input_CIM.input_ports): - _, mech_with_labels, __ = k.input_CIM._get_destination_node_for_input_CIM(port) + _, mech_with_labels, __ = k.input_CIM._get_destination_info_from_input_CIM(port) v[i] = k._parse_labels(inputs[k][i],mech_with_labels) _inputs.update({k:v}) else: @@ -10472,7 +10500,7 @@ def _get_inputs(comp, nesting_level=1, use_labels=False): in node._get_nested_nodes_with_same_roles_at_all_levels(node, NodeRole.INPUT))): input_values = [] for i, port in enumerate(node.input_CIM.input_ports): - _, mech, __ = node.input_CIM._get_destination_node_for_input_CIM(port) + _, mech, __ = node.input_CIM._get_destination_info_from_input_CIM(port) labels_dict = mech.input_labels_dict if labels_dict: labels = list(labels_dict[0].keys()) @@ -10561,7 +10589,7 @@ def get_results_by_nodes(self, warnings.warn(f"{alias} is aliased to get_results_by_nodes(); please use that in the future.") # Get all OUTPUT Nodes in (nested) Composition(s) - output_nodes = [self.output_CIM._get_source_node_for_output_CIM(port)[1] + output_nodes = [self.output_CIM._get_source_info_from_output_CIM(port)[1] for port in self.output_CIM.output_ports] # Get all values for all OUTPUT Nodes diff --git a/psyneulink/core/compositions/showgraph.py b/psyneulink/core/compositions/showgraph.py index c6a4f5e8374..a4fbe76eb7c 100644 --- a/psyneulink/core/compositions/showgraph.py +++ b/psyneulink/core/compositions/showgraph.py @@ -1493,7 +1493,7 @@ def _render_projection(_g, proj, sndr_label, rcvr_label, else: proj_color=self.inactive_projection_color else: - port, node, comp = cim._get_source_node_for_output_CIM(proj.receiver) + port, node, comp = cim._get_source_info_from_output_CIM(proj.receiver) if (node in comp.get_nodes_by_role(NodeRole.PROBE) and not composition.include_probes_in_output): proj_color=self.probe_color @@ -1846,7 +1846,7 @@ def find_rcvr_comp(r, c, l): # Get nested source node for direct projection to objective mechanism if isinstance(projection.sender.owner, CompositionInterfaceMechanism) and not show_cim: cim_output_port = projection.sender - proj_sndr, node, comp = cim_output_port.owner._get_source_node_for_output_CIM(cim_output_port) + proj_sndr, node, comp = cim_output_port.owner._get_source_info_from_output_CIM(cim_output_port) else: proj_sndr = projection.sender # MODIFIED 1/6/22 END @@ -1969,8 +1969,7 @@ def find_rcvr_comp(r, c, l): g.edge(agent_rep_label, ctlr_label, color=agent_rep_color, penwidth=agent_rep_width) g.edge(ctlr_label, agent_rep_label, color=agent_rep_color, penwidth=agent_rep_width) - # get any other incoming edges to controller - # (i.e., other than from ObjectiveMechanism or directly monitored nodes) + # get any state_feature projections and any other incoming edges to controller senders = set() # FIX: 11/3/21 - NEED TO MODIFY ONCE OUTCOME InputPorts ARE MOVED for i in controller.input_ports[controller.num_outcome_input_ports:]: diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 9cb07e72c4b..a1588572a28 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -744,6 +744,111 @@ def test_transfer_mechanism_and_ocm_variations( class TestControlMechanisms: + messages = ["The 'state_features' argument has been specified for 'OptimizationControlMechanism-0', " + "that is being configured to use a Composition ('OUTER COMP') as its 'agent_rep'). " + "This overrides automatic assignment of its 'state_features' as inputs to 'OUTER COMP' " + "when it is executed. If they are not properly configured, it will cause an error. " + "Remove this specification from the constructor for 'OptimizationControlMechanism-0' " + "to automatically configure its 'state_features' to be the external inputs to 'OUTER COMP'.", + + '\'Attempt to shadow the input to a node (IB) in a nested Composition of OUTER COMP ' + 'that is not an INPUT Node of that Composition is not currently supported.\'', + + '"OptimizationControlMechanism-0, being used as controller for model-based optimization of OUTER COMP, ' + 'has \'state_features\' specified ([\'Shadowed input of EXT\']) that are missing from the ' + 'Composition or any nested within it."', + + '"OptimizationControlMechanism-0, being used as controller for model-based optimization of OUTER COMP, ' + 'has \'state_features\' specified ([\'EXT[OutputPort-0]\']) that are missing from the ' + 'Composition or any nested within it."' + ] + + state_feature_specs = ['legal_feature', 'misplaced_shadow', 'ext_shadow', 'ext_output_port'] + + state_feature_args = [ + (state_feature_specs[0], messages[0], UserWarning), + (state_feature_specs[1], messages[1], pnl.CompositionError), + (state_feature_specs[2], messages[2], pnl.OptimizationControlMechanismError), + (state_feature_specs[3], messages[3], pnl.OptimizationControlMechanismError) + ] + + @pytest.mark.control + @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x for x in state_feature_specs]) + def test_ocm_state_input_ports_warnings_and_errors(self, state_feature_args): + ia = pnl.ProcessingMechanism(name='IA') + ib = pnl.ProcessingMechanism(name='IB') + ic = pnl.ProcessingMechanism(name='IC') + oa = pnl.ProcessingMechanism(name='OA') + ob = pnl.ProcessingMechanism(name='OB') + oc = pnl.ProcessingMechanism(name='OC') + ext = pnl.ProcessingMechanism(name='EXT') + icomp = pnl.Composition(pathways=[ia,ib,ic], name='INNER COMP') + ocomp = pnl.Composition(pathways=[icomp], name='OUTER COMP') + ocomp.add_linear_processing_pathway([oa,oc]) + ocomp.add_linear_processing_pathway([ob,oc]) + state_features_dict = {'legal_feature':ia.input_port, + 'misplaced_shadow':ib.input_port, + 'ext_shadow':ext.input_port, + 'ext_output_port':ext.output_port} + state_features = state_features_dict[state_feature_args[0]] + message = state_feature_args[1] + ocm = pnl.OptimizationControlMechanism(state_features=state_features, + objective_mechanism=[ic,ib], + function=pnl.GridSearch(), + control_signals=[pnl.ControlSignal(modulates=(pnl.SLOPE,ia), + allocation_samples=[10, 20, 30]), + pnl.ControlSignal(modulates=(pnl.INTERCEPT,oc), + allocation_samples=[10, 20, 30])]) + if state_feature_args[2] is UserWarning: + with pytest.warns(UserWarning) as warning: + ocomp.add_controller(ocm) + ocomp.run() + assert warning[9].message.args[0] == message + else: + with pytest.raises(state_feature_args[2]) as error: + ocomp.add_controller(ocm) + ocomp.run() + assert message in str(error.value) + + @pytest.mark.control + def test_ocm_state_and_state_dict(self): + ia = pnl.ProcessingMechanism(name='IA') + ib = pnl.ProcessingMechanism(name='IB') + ic = pnl.ProcessingMechanism(name='IC') + oa = pnl.ProcessingMechanism(name='OA') + ob = pnl.ProcessingMechanism(name='OB') + oc = pnl.ProcessingMechanism(name='OC') + icomp = pnl.Composition(pathways=[ia,ib,ic], name='INNER COMP') + ocomp = pnl.Composition(pathways=[icomp], name='OUTER COMP') + ocomp.add_linear_processing_pathway([oa,oc]) + ocomp.add_linear_processing_pathway([ob,oc]) + ocm = pnl.OptimizationControlMechanism( + state_features=[ia.input_port, # Note: these state_features will not execute properly + ib.output_port, # they are only for testing + oc], + objective_mechanism=[ic,ib], + function=pnl.GridSearch(), + allow_probes=True, + control_signals=[pnl.ControlSignal(modulates=(pnl.SLOPE,ia), + allocation_samples=[10, 20, 30]), + pnl.ControlSignal(modulates=[(pnl.INTERCEPT,oc),(pnl.SLOPE, oc)], + allocation_samples=[10, 20, 30]), + ] + ) + ocomp.add_controller(ocm) + assert np.allclose(ocm.state, [[0.], [0.], [0.], [1.], [1.]]) + assert len(ocm.state_dict) == 6 + keys = list(ocm.state_dict.keys()) + values = list(ocm.state_dict.values()) + for key, value in ocm.state_dict.items(): + ocm.state[key[3]] == value + assert keys[0] == (ia.input_port, ia, icomp ,0) + assert keys[1] == (ib.output_port, ib, icomp, 1) + assert keys[2] == (oc.input_port, oc, ocomp, 2) + assert keys[3] == (ia.parameter_ports[pnl.SLOPE], ia, icomp, 3) + assert keys[4] == (oc.parameter_ports[pnl.INTERCEPT], oc, ocomp, 4) + assert keys[5] == (oc.parameter_ports[pnl.SLOPE], oc, ocomp, 4) + def test_modulation_of_control_signal_intensity_cost_function_MULTIPLICATIVE(self): # tests multiplicative modulation of default intensity_cost_function (Exponential) of # a ControlMechanism's default function (TransferWithCosts); @@ -2739,6 +2844,7 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, bad_state_featu assert error_text in str(error.value) ocomp.run() + class TestSampleIterator: def test_int_step(self): From b856109ee84bddd6c8acb698e717f1c2653dde39 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 5 Jan 2022 02:34:05 -0500 Subject: [PATCH 090/285] llvm, functions/UDF: Search the source file to find the function definition AST node This will be needed to compile lambda functions that can share source lines with other AST nodes and can't be parsed in isolation. Signed-off-by: Jan Vesely --- .../functions/userdefinedfunction.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index 25ff0a7a9d6..acff76b3d49 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -11,7 +11,7 @@ import numpy as np import typecheck as tc -from inspect import signature, _empty, getsourcelines, getclosurevars +from inspect import signature, _empty, getsourcelines, getsourcefile, getclosurevars import ast from psyneulink.core.components.functions.function import FunctionError, Function_Base @@ -663,12 +663,17 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, closure_vars = getclosurevars(self.custom_function) assert len(closure_vars.nonlocals) == 0, "Compiling functions with non-local variables is not supported!" - srclines = getsourcelines(self.custom_function)[0] - # strip preceding 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) + srcfile = getsourcefile(self.custom_function) + first_line = getsourcelines(self.custom_function)[1] + + with open(srcfile) as f: + for node in ast.walk(ast.parse(f.read(), srcfile)): + if getattr(node, 'lineno', -1) == first_line and isinstance(node, (ast.FunctionDef)): + func_ast = node + break + func_ast = None + + assert func_ast is not None, "UDF function source code not found" func_globals = closure_vars.globals assert len(func_globals) == 0 or ( From 59a7c47bd159448e6bbb26831dfaa077f7d1fc85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 15:05:45 +0000 Subject: [PATCH 091/285] requirements: update sphinx-autodoc-typehints requirement (#2283) --- doc_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_requirements.txt b/doc_requirements.txt index 88964085f9a..043ea79e043 100644 --- a/doc_requirements.txt +++ b/doc_requirements.txt @@ -1,3 +1,3 @@ psyneulink-sphinx-theme<1.2.3.1 sphinx<4.2.1 -sphinx_autodoc_typehints<1.15.0 +sphinx_autodoc_typehints<1.16.0 From e97593d07b436e8f4cbda00bbfc31c3c66d3b81b Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 11 Jan 2022 11:26:57 -0500 Subject: [PATCH 092/285] llvm, functions/UDF: Move Function setup code to UDF visitor _gen_llvm_function_body handles only interaction with the rest of the code framework. Signed-off-by: Jan Vesely --- .../functions/userdefinedfunction.py | 12 ++-------- psyneulink/core/llvm/codegen.py | 24 +++++++++++++++++-- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index acff76b3d49..e5812580c4f 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -681,17 +681,9 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, "Compiling functions with global variables is not supported! ({})".format(closure_vars.globals) func_params = {param_id: pnlvm.helpers.get_param_ptr(builder, self, params, param_id) for param_id in self.llvm_param_ids} - udf_block = builder.append_basic_block(name="udf_body") - udf_builder = pnlvm.ir.IRBuilder(udf_block) - - pnlvm.codegen.UserDefinedFunctionVisitor(ctx, builder, udf_builder, func_globals, func_params, arg_in, arg_out).visit(func_ast) - # After we're done with allocating variable stack space, jump to the code - builder.branch(udf_block) + pnlvm.codegen.UserDefinedFunctionVisitor(ctx, builder, func_globals, func_params, arg_in, arg_out).visit(func_ast) + # The generic '_gen_llvm' will append another ret void to this block post_block = builder.append_basic_block(name="post_udf") - # If the function didn't use return as the last statement jump back to the outer block - if not udf_builder.block.is_terminated: - udf_builder.branch(post_block) - builder.position_at_start(post_block) return builder diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index 1ad7df72155..03bb994378d 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -22,10 +22,9 @@ from .debug import debug_env class UserDefinedFunctionVisitor(ast.NodeVisitor): - def __init__(self, ctx, var_builder, builder, func_globals, func_params, arg_in, arg_out): + def __init__(self, ctx, builder, func_globals, func_params, arg_in, arg_out): self.ctx = ctx self.builder = builder - self.var_builder = var_builder self.func_params = func_params self.arg_in = arg_in self.arg_out = arg_out @@ -100,6 +99,27 @@ def visit_arguments(self, node): else: self.register[param.arg] = self.func_params[param.arg] + def visit_FunctionDef(self, node): + # the current position will be used to create temp space + # for local variables. This block dominates all others + # generated by this visitor. + self.var_builder = self.builder + + # Create a new basic block to house the generated code + udf_block = self.builder.append_basic_block(name="udf_body") + self.builder = ir.IRBuilder(udf_block) + + super().generic_visit(node) + + if not self.builder.block.is_terminated: + # the function didn't use return as the last statement + # e.g. only includes 'return' statements in if blocks + self.builder.ret_void() + + # No more local variables at this point + self.var_builder.branch(udf_block) + return self.builder + def visit_Add(self, node): def _add(builder, x, y): assert helpers.is_floating_point(x) From 1f3295e1abfed1f7b57c3fcf4658ea97c00a1efb Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 11 Jan 2022 12:28:26 -0500 Subject: [PATCH 093/285] llvm, functions/UDF: Add support for compiling lambda functions Signed-off-by: Jan Vesely --- .../core/components/functions/userdefinedfunction.py | 2 +- psyneulink/core/llvm/codegen.py | 10 ++++++++++ tests/functions/test_user_defined_func.py | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index e5812580c4f..4f0ae2dc1a8 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -668,7 +668,7 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, with open(srcfile) as f: for node in ast.walk(ast.parse(f.read(), srcfile)): - if getattr(node, 'lineno', -1) == first_line and isinstance(node, (ast.FunctionDef)): + if getattr(node, 'lineno', -1) == first_line and isinstance(node, (ast.FunctionDef, ast.Lambda)): func_ast = node break func_ast = None diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index 03bb994378d..d80a2faa4c4 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -120,6 +120,16 @@ def visit_FunctionDef(self, node): self.var_builder.branch(udf_block) return self.builder + def visit_Lambda(self, node): + self.visit(node.args) + expr = self.visit(node.body) + + # store the lambda expression in the result and terminate + self.builder.store(expr, self.arg_out) + self.builder.ret_void() + + return self.builder + def visit_Add(self, node): def _add(builder, x, y): assert helpers.is_floating_point(x) diff --git a/tests/functions/test_user_defined_func.py b/tests/functions/test_user_defined_func.py index a11907e944c..5deae846a06 100644 --- a/tests/functions/test_user_defined_func.py +++ b/tests/functions/test_user_defined_func.py @@ -295,6 +295,9 @@ def condValReturn(variable, param1, param2): val = param2 + 0.3 return val +def lambdaGen(): + return lambda var, param1, param2: var + param1 * param2 + @pytest.mark.parametrize("func,var,params,expected", [ (simpleFun, [1, 3], {"param1":None, "param2":3}, [5, 9]), @@ -302,6 +305,7 @@ def condValReturn(variable, param1, param2): (condReturn, [1], {"param1":1, "param2":2}, [1.5]), (condValReturn, [0], {"param1":1, "param2":2}, [2.3]), (condValReturn, [1], {"param1":1, "param2":2}, [1.5]), + (lambdaGen(), [3], {"param1":3, "param2":-0.5}, [1.5]), ]) @pytest.mark.benchmark(group="Function UDF") def test_user_def_func(func, var, params, expected, func_mode, benchmark): From 75c69034bcd0f2854bee4fe419c8cf9fd9329cda Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 11 Jan 2022 12:46:19 -0500 Subject: [PATCH 094/285] tests/ProcessingMechanism: Test compiled MAX_VAL output_port Signed-off-by: Jan Vesely --- tests/mechanisms/test_processing_mechanism.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mechanisms/test_processing_mechanism.py b/tests/mechanisms/test_processing_mechanism.py index 7741cc0a102..f540b97bffe 100644 --- a/tests/mechanisms/test_processing_mechanism.py +++ b/tests/mechanisms/test_processing_mechanism.py @@ -246,6 +246,7 @@ class TestProcessingMechanismStandardOutputPorts: @pytest.mark.parametrize("op, expected", [(MAX_ONE_HOT, [0, 2, 0]), (MAX_INDICATOR, [0, 1, 0]), (MAX_ABS_INDICATOR, [0, 0, 1]), + (MAX_VAL, [2]), ], ids=lambda x: x if isinstance(x, str) else "") def test_output_ports(self, mech_mode, op, expected, benchmark): @@ -262,7 +263,6 @@ def test_output_ports(self, mech_mode, op, expected, benchmark): (MEDIAN, [2]), (STANDARD_DEVIATION, [1.24721913]), (VARIANCE, [1.55555556]), - (MAX_VAL, [2]), (MAX_ABS_VAL, [4]), (MAX_ABS_ONE_HOT, [0, 0, 4]), (PROB, [0, 2, 0]), From eb4a762f55a05b6894e026fc5f34564f187058c7 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 11 Jan 2022 13:01:51 -0500 Subject: [PATCH 095/285] llvm/debug: Rename IR dump option 'opt' -> 'llvm-opt' Avoid conflict with option setting compiler optimization level. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/debug.py | 2 +- psyneulink/core/llvm/jit_engine.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/llvm/debug.py b/psyneulink/core/llvm/debug.py index ec9ccaa50f6..7db9a38c1c8 100644 --- a/psyneulink/core/llvm/debug.py +++ b/psyneulink/core/llvm/debug.py @@ -39,7 +39,7 @@ Compiled code dump: * "llvm" -- dumps LLVM IR into a file (named after the dumped module). Code is dumped both after module generation and linking into global module. - * "opt" -- dump LLVM IR after running through the optimization passes + * "llvm-opt" -- dump LLVM IR after running through the optimization passes * "isa" -- dump machine specific ISA """ diff --git a/psyneulink/core/llvm/jit_engine.py b/psyneulink/core/llvm/jit_engine.py index faf7be4918a..c18146237ac 100644 --- a/psyneulink/core/llvm/jit_engine.py +++ b/psyneulink/core/llvm/jit_engine.py @@ -177,7 +177,7 @@ def opt_and_add_bin_module(self, module): if "time_stat" in debug_env: print("Time to optimize LLVM module bundle '{}': {}".format(module.name, finish - start)) - if "opt" in self.__debug_env: + if "llvm-opt" in self.__debug_env: with open(self.__class__.__name__ + '-' + str(self.__optimized_modules) + '.opt.ll', 'w') as dump_file: dump_file.write(str(module)) From f7efbe5bc0d72ce744ad4ad930bf4a6416dbb91e Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 11 Jan 2022 18:58:32 -0500 Subject: [PATCH 096/285] OCM: improve simulation context setup (#2279) - initialize context for when an agent_rep uses a controller other than its self.controller (ex. test_nested_composition_as_agent_rep) - add Composition._initialize_as_agent_rep and Composition._clean_up_as_agent_rep --- .../control/optimizationcontrolmechanism.py | 44 ++++++++++++++----- psyneulink/core/compositions/composition.py | 21 +++++++++ 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 16a77384c04..a00c98fa58d 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1930,9 +1930,18 @@ def _execute(self, variable=None, context=None, runtime_params=None): # freeze the values of current context, because they can be changed in between simulations, # and the simulations must start from the exact spot - self.agent_rep._initialize_from_context(self._get_frozen_context(context), - base_context=context, - override=True) + frozen_context = self._get_frozen_context(context) + + alt_controller = None + if self.agent_rep.controller is None: + try: + alt_controller = context.composition.controller + except AttributeError: + pass + + self.agent_rep._initialize_as_agent_rep( + frozen_context, base_context=context, alt_controller=alt_controller + ) # Get control_allocation that optimizes net_outcome using OptimizationControlMechanism's function # IMPLEMENTATION NOTE: skip ControlMechanism._execute since it is a stub method that returns input_values @@ -1945,7 +1954,7 @@ def _execute(self, variable=None, context=None, runtime_params=None): ) # clean up frozen values after execution - self.agent_rep._delete_contexts(self._get_frozen_context(context)) + self.agent_rep._clean_up_as_agent_rep(frozen_context, alt_controller=alt_controller) optimal_control_allocation = np.array(optimal_control_allocation).reshape((len(self.defaults.value), 1)) if self.function.save_samples: @@ -1959,7 +1968,12 @@ def _execute(self, variable=None, context=None, runtime_params=None): def _get_frozen_context(self, context=None): return Context(execution_id=f'{context.execution_id}{EID_FROZEN}') - def _set_up_simulation(self, base_context=Context(execution_id=None), control_allocation=None): + def _set_up_simulation( + self, + base_context=Context(execution_id=None), + control_allocation=None, + alt_controller=None + ): sim_context = copy.copy(base_context) sim_context.execution_id = self.get_next_sim_id(base_context, control_allocation) @@ -1968,13 +1982,17 @@ def _set_up_simulation(self, base_context=Context(execution_id=None), control_al except AttributeError: self.parameters.simulation_ids._set([sim_context.execution_id], base_context) - self.agent_rep._initialize_from_context(sim_context, self._get_frozen_context(base_context), override=False) + self.agent_rep._initialize_as_agent_rep( + sim_context, + base_context=self._get_frozen_context(base_context), + alt_controller=alt_controller + ) return sim_context - def _tear_down_simulation(self, sim_context=None): + def _tear_down_simulation(self, sim_context, alt_controller=None): if not self.agent_rep.parameters.retain_old_simulation_data._get(): - self.agent_rep._delete_contexts(sim_context, check_simulation_storage=True) + self.agent_rep._clean_up_as_agent_rep(sim_context, alt_controller=alt_controller) def evaluate_agent_rep(self, control_allocation, context=None, return_results=False): """Call `evaluate ` method of `agent_rep ` @@ -2008,11 +2026,17 @@ def evaluate_agent_rep(self, control_allocation, context=None, return_results=Fa # agent_rep is a Composition (since runs_simulations = True) if self.agent_rep.runs_simulations: + alt_controller = None + if self.agent_rep.controller is None: + try: + alt_controller = context.composition.controller + except AttributeError: + pass # KDM 5/20/19: crudely using default here because it is a stateless parameter # and there is a bug in setting parameter values on init, see TODO note above # call to self._instantiate_defaults around component.py:1115 if self.defaults.search_statefulness: - new_context = self._set_up_simulation(context, control_allocation) + new_context = self._set_up_simulation(context, control_allocation, alt_controller) else: new_context = context @@ -2031,7 +2055,7 @@ def evaluate_agent_rep(self, control_allocation, context=None, return_results=Fa return_results=return_results) context.composition = old_composition if self.defaults.search_statefulness: - self._tear_down_simulation(new_context) + self._tear_down_simulation(new_context, alt_controller) # FIX: THIS SHOULD BE REFACTORED TO BE HANDLED THE SAME AS A Composition AS agent_rep # If results of the simulation should be returned then, do so. agent_rep's evaluate method will diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index c2616491ab7..29bd7ab522a 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -10895,6 +10895,27 @@ def _delete_contexts(self, *contexts, check_simulation_storage=False, visited=No except AttributeError: self.scheduler._delete_counts(c) + def _initialize_as_agent_rep(self, context, base_context, alt_controller=None): + assert self.controller is None or alt_controller is None + + _initialized = set() # avoid reinitializing shared dependencies below + self._initialize_from_context( + context, base_context=base_context, override=True, visited=_initialized + ) + if alt_controller is not None: + # evaluation will be done with a controller from another composition + alt_controller._initialize_from_context( + context, base_context=base_context, override=True, visited=_initialized + ) + + def _clean_up_as_agent_rep(self, context, alt_controller=None): + _deleted = set() # avoid traversing shared dependencies below + self._delete_contexts(context, visited=_deleted, check_simulation_storage=True) + if alt_controller is not None: + alt_controller._delete_contexts( + context, visited=_deleted, check_simulation_storage=True + ) + # endregion EXECUTION # ****************************************************************************************************************** From fbb8d1c9ce0fa9387cbfa09fca5a01995f809f89 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 12 Jan 2022 10:25:06 -0500 Subject: [PATCH 097/285] llvm, functions/OneHot: MAX_ABS_VAL should return absolute value of the greatest magnitutde (#2287) Enable compiled test of MAX_ABS_ONE_HOT output port. (#1780) Signed-off-by: Jan Vesely --- .../core/components/functions/nonstateful/selectionfunctions.py | 2 +- tests/functions/test_selection.py | 2 ++ tests/mechanisms/test_processing_mechanism.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/selectionfunctions.py b/psyneulink/core/components/functions/nonstateful/selectionfunctions.py index 56d95e09ac2..aff4dc5764f 100644 --- a/psyneulink/core/components/functions/nonstateful/selectionfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/selectionfunctions.py @@ -288,7 +288,7 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, cmp_op = ">=" cmp_prev = b1.call(fabs, [prev]) cmp_curr = b1.call(fabs, [current]) - val = current + val = b1.call(fabs, [current]) elif self.mode == MAX_INDICATOR: cmp_op = ">=" cmp_prev = prev diff --git a/tests/functions/test_selection.py b/tests/functions/test_selection.py index 1dc450f9989..3ca5059706a 100644 --- a/tests/functions/test_selection.py +++ b/tests/functions/test_selection.py @@ -19,6 +19,7 @@ test_data = [ (Functions.OneHot, test_var, {'mode':kw.MAX_VAL}, [0., 0., 0., 0., 0., 0., 0., 0., 0.92732552, 0.]), (Functions.OneHot, test_var, {'mode':kw.MAX_ABS_VAL}, [0., 0., 0., 0., 0., 0., 0., 0., 0.92732552, 0.]), + (Functions.OneHot, -test_var, {'mode':kw.MAX_ABS_VAL}, [0., 0., 0., 0., 0., 0., 0., 0., 0.92732552, 0.]), (Functions.OneHot, test_var, {'mode':kw.MAX_INDICATOR}, [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.]), (Functions.OneHot, test_var, {'mode':kw.MAX_ABS_INDICATOR}, [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.]), (Functions.OneHot, test_var, {'mode':kw.MIN_VAL}, [0., 0., 0., 0., 0., 0., 0., 0., 0., -0.23311696]), @@ -35,6 +36,7 @@ names = [ "OneHot MAX_VAL", "OneHot MAX_ABS_VAL", + "OneHot MAX_ABS_VAL_NEG", "OneHot MAX_INDICATOR", "OneHot MAX_ABS_INDICATOR", "OneHot MIN_VAL", diff --git a/tests/mechanisms/test_processing_mechanism.py b/tests/mechanisms/test_processing_mechanism.py index f540b97bffe..2d34f31538b 100644 --- a/tests/mechanisms/test_processing_mechanism.py +++ b/tests/mechanisms/test_processing_mechanism.py @@ -246,6 +246,7 @@ class TestProcessingMechanismStandardOutputPorts: @pytest.mark.parametrize("op, expected", [(MAX_ONE_HOT, [0, 2, 0]), (MAX_INDICATOR, [0, 1, 0]), (MAX_ABS_INDICATOR, [0, 0, 1]), + (MAX_ABS_ONE_HOT, [0, 0, 4]), (MAX_VAL, [2]), ], ids=lambda x: x if isinstance(x, str) else "") @@ -264,7 +265,6 @@ def test_output_ports(self, mech_mode, op, expected, benchmark): (STANDARD_DEVIATION, [1.24721913]), (VARIANCE, [1.55555556]), (MAX_ABS_VAL, [4]), - (MAX_ABS_ONE_HOT, [0, 0, 4]), (PROB, [0, 2, 0]), ], ids=lambda x: x if isinstance(x, str) else "") From 47dc2937d043e38e301688077b106aac83054754 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 12 Jan 2022 21:40:27 -0500 Subject: [PATCH 098/285] OCM: cleanup fix to e41f205316 (#2286) - remove unneeded call to _create_randomization_control_signal in Composition - simplify _create_randomization_control_signal --- .../control/optimizationcontrolmechanism.py | 24 +++++++++---------- psyneulink/core/compositions/composition.py | 22 +---------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index a00c98fa58d..f850d312657 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -875,7 +875,7 @@ from psyneulink.core.components.component import DefaultsFlexibility from psyneulink.core.components.functions.function import is_function_type from psyneulink.core.components.functions.nonstateful.optimizationfunctions import \ - GridSearch, OBJECTIVE_FUNCTION, SEARCH_SPACE + GridSearch, OBJECTIVE_FUNCTION, SEARCH_SPACE, RANDOMIZATION_DIMENSION from psyneulink.core.components.functions.nonstateful.transferfunctions import CostFunctions from psyneulink.core.components.mechanisms.mechanism import Mechanism from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import \ @@ -1783,11 +1783,11 @@ def _instantiate_control_signals(self, context): # MODIFIED 11/20/21 END self.output_ports[i] = control_signal - self._create_randomization_control_signal(context, set_control_signal_index=False) + self._create_randomization_control_signal(context) self.defaults.value = np.tile(control_signal.parameters.variable.default_value, (len(self.output_ports), 1)) self.parameters.control_allocation._set(copy.deepcopy(self.defaults.value), context) - def _create_randomization_control_signal(self, context, set_control_signal_index=True): + def _create_randomization_control_signal(self, context): if self.num_estimates: # must be SampleSpec in allocation_samples arg randomization_seed_mod_values = SampleSpec(start=1, stop=self.num_estimates, step=1) @@ -1836,15 +1836,6 @@ def _create_randomization_control_signal(self, context, set_control_signal_index # search_space must be a SampleIterator function_search_space.append(SampleIterator(randomization_seed_mod_values)) - # workaround for fact that self.function.reset call in - # _instantiate_attributes_after_function expects to use - # old/unset values when running _update_default_variable, - # which calls self.agent_rep.evaluate and is brittle. - if set_control_signal_index: - self.function.parameters.randomization_dimension._set( - randomization_control_signal_index, context - ) - def _instantiate_function(self, function, function_params=None, context=None): # this indicates a significant peculiarity of OCM, in that its function # corresponds to its value (control_allocation) rather than anything to @@ -1882,13 +1873,22 @@ def _instantiate_attributes_after_function(self, context=None): corrected_search_space = [SampleIterator(specification=search_space)] self.parameters.search_space._set(corrected_search_space, context) + try: + randomization_control_signal_index = self.control_signals.names.index(RANDOMIZATION_CONTROL_SIGNAL) + except ValueError: + randomization_control_signal_index = None + # Assign parameters to function (OptimizationFunction) that rely on OptimizationControlMechanism + # NOTE: as in this call, randomization_dimension must be set + # after search_space to avoid IndexError when getting + # num_estimates of function self.function.reset(**{ DEFAULT_VARIABLE: self.parameters.control_allocation._get(context), OBJECTIVE_FUNCTION: self.evaluate_agent_rep, # SEARCH_FUNCTION: self.search_function, # SEARCH_TERMINATION_FUNCTION: self.search_termination_function, SEARCH_SPACE: self.parameters.control_allocation_search_space._get(context), + RANDOMIZATION_DIMENSION: randomization_control_signal_index }) if isinstance(self.agent_rep, type): diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 29bd7ab522a..6c8d44a6246 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2596,8 +2596,7 @@ def input_function(env, result): from psyneulink.core.components.functions.nonstateful.transferfunctions import Identity from psyneulink.core.components.mechanisms.mechanism import Mechanism_Base, MechanismError, MechanismList from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import ControlMechanism -from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP, \ - RANDOMIZATION_CONTROL_SIGNAL, NUM_ESTIMATES +from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP from psyneulink.core.components.mechanisms.modulatory.learning.learningmechanism import \ LearningMechanism, ACTIVATION_INPUT_INDEX, ACTIVATION_OUTPUT_INDEX, ERROR_SIGNAL, ERROR_SIGNAL_INDEX from psyneulink.core.components.mechanisms.modulatory.modulatorymechanism import ModulatoryMechanism_Base @@ -7746,25 +7745,6 @@ def add_controller(self, controller:ControlMechanism, context=None): for node in self.nodes: self._instantiate_deferred_init_control(node, context) - # MODIFIED 1/4/22 OLD: - if hasattr(self.controller, NUM_ESTIMATES) and self.controller.num_estimates: - if RANDOMIZATION_CONTROL_SIGNAL not in self.controller.output_ports.names: - try: - self.controller._create_randomization_control_signal(context) - except AttributeError: - # ControlMechanism does not use RANDOMIZATION_CONTROL_SIGNAL - pass - else: - self.controller.function.parameters.randomization_dimension._set( - self.controller.output_ports.names.index(RANDOMIZATION_CONTROL_SIGNAL), - context - ) - self.controller.function.parameters.randomization_dimension._set( - self.controller.output_ports.names.index(RANDOMIZATION_CONTROL_SIGNAL), - context - ) - # MODIFIED 1/4/22 END - # ACTIVATE FOR COMPOSITION ----------------------------------------------------- self.node_ordering.append(controller) From 02f7007bb8ccd177db902a1cb22903fe46c498c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Jan 2022 00:31:25 +0000 Subject: [PATCH 099/285] requirements: update llvmlite requirement from <0.38 to <0.39 (#2288) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b17b7ef1e76..4423e0391c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ elfi<0.8.3 graphviz<0.20.0 grpcio<1.43.0 grpcio-tools<1.43.0 -llvmlite<0.38 +llvmlite<0.39 matplotlib<3.4.4 networkx<2.6 numpy<1.21.4, >=1.17.0 From 8a9fe9dccf129308a6c2d680e6cdb8ae5f9af4e6 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 15 Dec 2021 21:57:41 -0500 Subject: [PATCH 100/285] utilities: is_value_spec: exclude lists with functions --- psyneulink/core/globals/utilities.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/globals/utilities.py b/psyneulink/core/globals/utilities.py index 351775aed03..6f09bb09485 100644 --- a/psyneulink/core/globals/utilities.py +++ b/psyneulink/core/globals/utilities.py @@ -1379,9 +1379,15 @@ def get_values_as_lists(self, context=None): def is_value_spec(spec): + from psyneulink.core.components.component import Component + if isinstance(spec, (numbers.Number, np.ndarray)): return True - elif isinstance(spec, list) and is_numeric(spec): + elif ( + isinstance(spec, list) + and is_numeric(spec) + and not contains_type(spec, (Component, types.FunctionType)) + ): return True else: return False From 815fa68c8436e3ea191276ee8b6d3816d19a1612 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 15 Dec 2021 01:16:25 -0500 Subject: [PATCH 101/285] functions: noise properties based on numeric or function --- .../core/components/functions/function.py | 35 ++++++++- .../nonstateful/distributionfunctions.py | 4 +- .../functions/stateful/integratorfunctions.py | 6 +- .../functions/stateful/memoryfunctions.py | 14 +++- .../functions/stateful/statefulfunction.py | 10 ++- tests/mechanisms/test_mechanisms.py | 76 ++++++++++++++++++- 6 files changed, 133 insertions(+), 12 deletions(-) diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index 5b8cbb10ca7..98e5a63003a 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -163,7 +163,7 @@ from psyneulink.core.globals.registry import register_category from psyneulink.core.globals.utilities import ( convert_to_np_array, get_global_seed, object_has_single_value, parameter_spec, safe_len, - SeededRandomState + SeededRandomState, contains_type, is_instance_or_subclass ) __all__ = [ @@ -368,6 +368,39 @@ def _random_state_getter(self, owning_component, context): return current_state +def _noise_setter(value, owning_component, context): + def has_function(x): + return ( + is_instance_or_subclass(x, (Function_Base, types.FunctionType)) + or contains_type(x, (Function_Base, types.FunctionType)) + ) + + noise_param = owning_component.parameters.noise + value_has_function = has_function(value) + # initial set + if owning_component.is_initializing: + if value_has_function: + # is changing a parameter attribute like this ok? + noise_param.stateful = False + else: + default_value_has_function = has_function(noise_param.default_value) + + if default_value_has_function and not value_has_function: + warnings.warn( + 'Setting noise to a numeric value after instantiation' + ' with a value containing functions will not remove the' + ' noise ParameterPort or make noise stateful.' + ) + elif not default_value_has_function and value_has_function: + warnings.warn( + 'Setting noise to a value containing functions after' + ' instantiation with a numeric value will not create a' + ' noise ParameterPort or make noise stateless.' + ) + + return value + + class Function_Base(Function): """ Function_Base( \ diff --git a/psyneulink/core/components/functions/nonstateful/distributionfunctions.py b/psyneulink/core/components/functions/nonstateful/distributionfunctions.py index aa6e6ef9ba1..1d1d9c65a5d 100644 --- a/psyneulink/core/components/functions/nonstateful/distributionfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/distributionfunctions.py @@ -30,7 +30,7 @@ from psyneulink.core import llvm as pnlvm from psyneulink.core.components.functions.function import ( DEFAULT_SEED, Function_Base, FunctionError, - _random_state_getter, _seed_setter, + _random_state_getter, _seed_setter, _noise_setter ) from psyneulink.core.globals.keywords import \ ADDITIVE_PARAM, DIST_FUNCTION_TYPE, BETA, DIST_MEAN, DIST_SHAPE, DRIFT_DIFFUSION_ANALYTICAL_FUNCTION, \ @@ -1100,7 +1100,7 @@ class Parameters(DistributionFunction.Parameters): drift_rate = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) starting_point = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) threshold = Parameter(1.0, modulable=True) - noise = Parameter(0.5, modulable=True) + noise = Parameter(0.5, modulable=True, setter=_noise_setter) t0 = Parameter(.200, modulable=True) bias = Parameter(0.5, read_only=True, getter=_DriftDiffusionAnalytical_bias_getter) # this is read only because conversion is disabled for this function diff --git a/psyneulink/core/components/functions/stateful/integratorfunctions.py b/psyneulink/core/components/functions/stateful/integratorfunctions.py index 80144c6897e..81a612e1b3c 100644 --- a/psyneulink/core/components/functions/stateful/integratorfunctions.py +++ b/psyneulink/core/components/functions/stateful/integratorfunctions.py @@ -36,7 +36,7 @@ from psyneulink.core.components.functions.nonstateful.distributionfunctions import DistributionFunction from psyneulink.core.components.functions.function import ( DEFAULT_SEED, FunctionError, _random_state_getter, - _seed_setter, + _seed_setter, _noise_setter ) from psyneulink.core.components.functions.stateful.statefulfunction import StatefulFunction from psyneulink.core.globals.context import ContextFlags, handle_external_context @@ -214,7 +214,9 @@ class Parameters(StatefulFunction.Parameters): :type: ``float`` """ rate = Parameter(1.0, modulable=True, function_arg=True) - noise = Parameter(0.0, modulable=True, function_arg=True) + noise = Parameter( + 0.0, modulable=True, function_arg=True, setter=_noise_setter + ) previous_value = Parameter(np.array([0]), initializer='initializer', pnl_internal=True) initializer = Parameter(np.array([0]), pnl_internal=True) diff --git a/psyneulink/core/components/functions/stateful/memoryfunctions.py b/psyneulink/core/components/functions/stateful/memoryfunctions.py index 910e49545ba..dc77e18cee6 100644 --- a/psyneulink/core/components/functions/stateful/memoryfunctions.py +++ b/psyneulink/core/components/functions/stateful/memoryfunctions.py @@ -35,7 +35,7 @@ from psyneulink.core import llvm as pnlvm from psyneulink.core.components.functions.function import ( - DEFAULT_SEED, FunctionError, _random_state_getter, _seed_setter, is_function_type, EPSILON, + DEFAULT_SEED, FunctionError, _random_state_getter, _seed_setter, is_function_type, EPSILON, _noise_setter ) from psyneulink.core.components.functions.nonstateful.objectivefunctions import Distance from psyneulink.core.components.functions.nonstateful.selectionfunctions import OneHot @@ -201,7 +201,9 @@ class Parameters(StatefulFunction.Parameters): """ variable = Parameter([], pnl_internal=True, constructor_argument='default_variable') rate = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) - noise = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) + noise = Parameter( + 0.0, modulable=True, aliases=[ADDITIVE_PARAM], setter=_noise_setter + ) history = None initializer = Parameter(np.array([]), pnl_internal=True) @@ -1091,7 +1093,9 @@ class Parameters(StatefulFunction.Parameters): duplicate_threshold = Parameter(EPSILON, stateful=False, modulable=True) equidistant_entries_select = Parameter(RANDOM) rate = Parameter(1.0, modulable=True) - noise = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) + noise = Parameter( + 0.0, modulable=True, aliases=[ADDITIVE_PARAM], setter=_noise_setter + ) max_entries = Parameter(1000) random_state = Parameter(None, loggable=False, getter=_random_state_getter, dependencies='seed') seed = Parameter(DEFAULT_SEED, modulable=True, fallback_default=True, setter=_seed_setter) @@ -2151,7 +2155,9 @@ class Parameters(StatefulFunction.Parameters): duplicate_keys = Parameter(False) equidistant_keys_select = Parameter(RANDOM) rate = Parameter(1.0, modulable=True) - noise = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) + noise = Parameter( + 0.0, modulable=True, aliases=[ADDITIVE_PARAM], setter=_noise_setter + ) max_entries = Parameter(1000) random_state = Parameter(None, loggable=False, getter=_random_state_getter, dependencies='seed') seed = Parameter(DEFAULT_SEED, modulable=True, fallback_default=True, setter=_seed_setter) diff --git a/psyneulink/core/components/functions/stateful/statefulfunction.py b/psyneulink/core/components/functions/stateful/statefulfunction.py index e8b8213e619..d420cd4e930 100644 --- a/psyneulink/core/components/functions/stateful/statefulfunction.py +++ b/psyneulink/core/components/functions/stateful/statefulfunction.py @@ -27,7 +27,7 @@ from psyneulink.core import llvm as pnlvm from psyneulink.core.components.component import DefaultsFlexibility, _has_initializers_setter, ComponentsMeta from psyneulink.core.components.functions.nonstateful.distributionfunctions import DistributionFunction -from psyneulink.core.components.functions.function import Function_Base, FunctionError +from psyneulink.core.components.functions.function import Function_Base, FunctionError, _noise_setter from psyneulink.core.globals.context import handle_external_context from psyneulink.core.globals.keywords import STATEFUL_FUNCTION_TYPE, STATEFUL_FUNCTION, NOISE, RATE from psyneulink.core.globals.parameters import Parameter @@ -146,6 +146,12 @@ class StatefulFunction(Function_Base): # -------------------------------------- output, or a list or array of either of these, then noise is simply an offset that remains the same across all executions. + .. note:: + A ParameterPort for noise will only be generated, and the + noise Parameter itself will only be stateful, if the value + of noise is entirely numeric (contains no functions) at the + time of Mechanism construction. + owner : Component `component ` to which the Function has been assigned. @@ -191,7 +197,7 @@ class Parameters(Function_Base.Parameters): :default value: 1.0 :type: ``float`` """ - noise = Parameter(0.0, modulable=True) + noise = Parameter(0.0, modulable=True, setter=_noise_setter) rate = Parameter(1.0, modulable=True) previous_value = Parameter(np.array([0]), initializer='initializer', pnl_internal=True) initializer = Parameter(np.array([0]), pnl_internal=True) diff --git a/tests/mechanisms/test_mechanisms.py b/tests/mechanisms/test_mechanisms.py index 34569eb6ee9..7c9e7249b63 100644 --- a/tests/mechanisms/test_mechanisms.py +++ b/tests/mechanisms/test_mechanisms.py @@ -45,7 +45,7 @@ def test_value_shapes(self, mechanism_type, default_variable, mechanism_value, f 'noise', [pnl.GaussianDistort, pnl.NormalDist] ) - def test_noise_variations(self, noise): + def test_noise_assignment_equivalence(self, noise): t1 = pnl.TransferMechanism(name='t1', size=2, noise=noise()) t2 = pnl.TransferMechanism(name='t2', size=2) t2.integrator_function.parameters.noise.set(noise()) @@ -56,6 +56,80 @@ def test_noise_variations(self, noise): for _ in range(5): np.testing.assert_equal(t1.execute([1, 1]), t2.execute([1, 1])) + @pytest.mark.parametrize( + 'noise, included_parameter_ports, excluded_parameter_ports, noise_statefulness', + [ + (0, ['noise'], ['seed'], True), + ([0], ['noise'], ['seed'], True), + ([0, 0], ['noise'], ['seed'], True), + ([0, pnl.NormalDist()], [], ['noise', 'seed'], False), + (pnl.NormalDist, ['seed'], ['noise'], False), + ([pnl.NormalDist(), pnl.NormalDist()], [], ['noise', 'seed'], False), + ] + ) + def test_numeric_noise_specifications( + self, + noise, + included_parameter_ports, + excluded_parameter_ports, + noise_statefulness + ): + try: + size = len(noise) + except TypeError: + size = 1 + + t = pnl.TransferMechanism(size=size, noise=noise) + + assert all(p in t.parameter_ports for p in included_parameter_ports) + assert all(p not in t.parameter_ports for p in excluded_parameter_ports) + + assert t.parameters.noise.stateful is noise_statefulness + + @pytest.mark.parametrize( + 'noise', + [ + [0, pnl.NormalDist()], + pnl.NormalDist, + [pnl.NormalDist(), pnl.NormalDist()] + ] + ) + def test_noise_change_warning_to_numeric(self, noise): + try: + size = len(noise) + except TypeError: + size = 1 + + t = pnl.TransferMechanism(size=size, noise=noise) + + with pytest.warns( + UserWarning, + match='Setting noise to a numeric value after instantiation.*' + ): + t.parameters.noise.set(0) + + @pytest.mark.parametrize( + 'noise', + [ + 0, + [0], + [0, 0], + ] + ) + def test_noise_change_warning_to_function(self, noise): + try: + size = len(noise) + except TypeError: + size = 1 + + t = pnl.TransferMechanism(size=size, noise=noise) + + with pytest.warns( + UserWarning, + match='Setting noise to a value containing functions after instantiation.*' + ): + t.parameters.noise.set(pnl.NormalDist) + class TestMechanismFunctionParameters: f = pnl.Linear() From 1f5a70dd8a699b92f395492bcc5e21b1e419f745 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sat, 15 Jan 2022 12:42:35 -0500 Subject: [PATCH 102/285] Feat/ocm/state feat dict (#2291) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * • showgraph.py: - _assign_incoming_edges(): allow input_CIM of outermost comp to render as node * • showgraph.py: - _assign_incoming_edges(): allow input_CIM of outermost comp to render as node - _assign_controller_components(): allow direct projections from nested nodes to OCM or ObjectiveMechanism when show_cim=False * • test_show_graph.py: add test_projections_from_nested_comp_to_ocm_or_obj_mech * - * • optimizationcontrolmechanism.py - add state and state_dict properties • composition.py: - add _get_source() - add _get_destination() • compositioninterfacemechanism.py: - add _get_modulated_info_from_parameter_CIM() - _get_source_node_for_output_CIM -> _get_source_info_from_output_CIM - _get_destination_node_for_input_CIM -> _get_destination_info_from_input_CIM * - optimizationcontrolmechanism.py: docstring additions for state and state_dict * • optimizationcontrolmechanism.py: - state_dict: fix bug in which comp for state_feature using output_port was incorrect - _parse_state_feature_specs(): fix bug in which state_feature using output_port did not get assigned a Projection • * • optimizationcontrolmechanism.py: -_update_state_input_ports_for_controller: validate state_features that specify an output_port (vs. shadow) * - * • test_control.py: add test_ocm_state_input_ports_warnings_and_errors * - * • conf.py: restore autodoc_typehints = 'none' * • optimizationcontrolmechanism.py: _update_state_input_ports_for_controller: - add check of against format of inputs required by agent_rep.run * - * • composition.py: - add_controller(): fix bug in which state_features that receive from the outputport of a node in the same composition were not being activated * - * - * • optimizationcontrolmechanism.py: _update_state_input_ports_for_controller: modified warning message for use of state_features * • optimizationcontrolmechanism.py: _update_state_input_ports_for_controller: modified warning message for use of state_features * - * • composition.py: - _build_predicted_input_dicts(): - add controller as argument - support regular (non-shadowed) inputs (i.e., from output_ports of other nodes) * • optimizationcontrolmechanism.py: - _build_predicted_inputs_dict(): support dict specification of state_features • composition.py: - _build_predicted_input_dicts(): - support use of keys for inputs dict from dict specified for state_features of OCM * • test_control.py: test_ocm_warnings_and_errors -> test_ocm_state_feature_specs_and_warnings_and_errors() added tests for dict spec of ocm state_features * • composition.py: - _build_predicted_inputs_dict(): handle SHADOW_INPUTS format in state_features * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add explicit test for SHADOW_INPUTS format * - * - * - * • composition.py: - _get_controller: uncommented and put to use * - * - * • composition.py: - evaluate(): controller = self._get_controller() self.controller -> controller • test_control.py: - test_nested_composition_as_agent_rep() passes (though may need some adjusting) * • test_control.py: - test_nested_composition_as_agent_rep() - cleaned up * - * • optimizationcontrolmechanism.py: docstring edits * • composition.py: - _validate_input_dict_node_roles -> _implement_input_dict * • composition.py: - _implement_input_dict -> _instantiate_input_dict * • composition.py: - _parse_dict -> _parse_input_dict * - * • optimizationcontrolmechanism.py: docstring edits * • optimizationcontrolmechanism.py: docstring edits * - * - * - * - * • inputport.py: add default_input Parameter docstring edits • port.py: _execute(): use default_input for InputPorts if specified * • test_input_ports.py: add test_default_input -- failing * - * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): set InputPort.default_input=DEFAULT_VARIABLE if state_feature specification is numerical * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): set InputPort.default_input=DEFAULT_VARIABLE if state_feature specification is numerical * • optimizationcontrolmechanism.py: _parse_state_feature_spec: fix bug with attempt to get nodes of CFA * • optimizationcontrolmechanism.py: - _update_state_input_ports_for_controller: error message edits • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors: add test for numerical assignment of state_feature * • test_control.py: - test_ocm_state_and_state_dict: numerical assignment to state_feature * • test_control.py: - test_default_input: passes (but has comments with some desired refactoring * - * - * • composition.py: - add_node(): if NodeRole.INTERNAL specified, assign internal_only to all InputPorts - _determine_node_roles(): if if NodeRole.INTERNAL specified, preclude NodeRole.INPUT * • composition.py: - add_node(): if NodeRole.INTERNAL specified, assign internal_only to all InputPorts - _determine_node_roles(): if if NodeRole.INTERNAL specified, preclude NodeRole.INPUT * • composition.py: - add_node(): if NodeRole.INTERNAL specified, assign internal_only to all InputPorts - _determine_node_roles(): if if NodeRole.INTERNAL specified, preclude NodeRole.INPUT * - Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- .../core/components/mechanisms/mechanism.py | 19 + .../control/optimizationcontrolmechanism.py | 511 ++++++++++------ .../compositioninterfacemechanism.py | 2 +- psyneulink/core/components/ports/inputport.py | 123 ++-- psyneulink/core/components/ports/port.py | 13 +- psyneulink/core/compositions/composition.py | 163 +++-- .../compositionfunctionapproximator.py | 10 +- psyneulink/core/globals/keywords.py | 3 +- tests/composition/test_control.py | 564 ++++++++++-------- tests/json/model_with_control.py | 2 + tests/ports/test_input_ports.py | 31 +- 11 files changed, 879 insertions(+), 562 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 268a91ed199..e6feee7f200 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2490,9 +2490,28 @@ def execute(self, if input is not None: input = convert_all_elements_to_np_array(input) + # MODIFIED 1/14/22 NEW: + else: + input = self.defaults.variable + # MODIFIED 1/14/22 END if input is None: + # MODIFIED 1/14/22 OLD: input = self.defaults.variable + # # MODIFIED 1/14/22 NEW: Handle None on port-by-port basis (using default_input attribute) + # if 'Parameter_CIM' in self.name: + # input = self.defaults.variable + # else: + # input = self._update_input_ports(context=context) + # # MODIFIED 1/14/22 NEWER: Handle None on port-by-port basis (using default_input attribute) + # if self.path_afferents: + # input = [] + # for input_port in self.input_ports: + # if input_port.path_afferents or input_port.default_input: + # input.append(self.defaults.variable[i]) + # else: + # input.append(None) + # MODIFIED 1/14/22 END # FIX: this input value is sent to input CIMs when compositions are nested # variable should be based on afferent projections variable = self._get_variable_from_input(input, context) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index f850d312657..30dfb9ca6ea 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -214,10 +214,11 @@ `agent_rep ` when used, together with a selected `control_allocation `, to estimate or predict the Composition's `net_outcome `. These are used to construct the `state_input_ports - ` for the OptimizationControlMechanism, that provide the - `agent_rep` with its input, and thus the specification requirements for - **state_features** depend on whether the `agent_rep` is a `Composition` - or a `CompositionFunctionApproximator`: + ` for the OptimizationControlMechanism, the `values ` + of which are assigned to `state_feature_values ` and provided to + the `agent_rep ` as its input when it is evaluated. Accordingly, the + specification requirements for **state_features** depend on whether the + `agent_rep` is a `Composition` or a `CompositionFunctionApproximator`: .. _OptimizationControlMechanism_Agent_Rep_Composition: @@ -233,22 +234,25 @@ ` for the optimization process; if the `controller_mode ` is *BEFORE*, then the inputs from the previous trial are used. + .. _OptimizationControlMechanism_State_Features_Explicit_Specification: + The **state_features** argument can also be specified explicitly, using the formats described below. This is - useful if different functions need to be assigned to different `state_input_ports - ` used to generate the corresponding `state_feature_values - state_feature_values ` (see `below - `). However, doing so overrides the automatic + useful if the values of the `state_input_ports ` are something + other than the inputs to the `agent_rep `, or if different functions need + to be assigned to different `state_input_ports ` used to generate + the corresponding `state_feature_values state_feature_values ` + (see `below `). However, doing so overrides the automatic assignment of all state_features, and so a complete and appropriate set of specifications must be provided (see note below). .. _OptimizationControlMechanism_State_Features_Shapes: .. note:: - If **state_features** *are* specified explicitly when the `agent_rep ` + If **state_features** are specified explicitly when the `agent_rep ` is a Composition, there must be one for every `InputPort` of every `INPUT ` `Node ` in that Composition, and these must match -- both individually, and in their order -- the `inputs to the Composition `) required by its `run ` - method. Failure to do so generates an error indicating this. + method. Failure to do so generates an error. .. _OptimizationControlMechanism_Selective_Input: @@ -260,9 +264,9 @@ `) such that those assigned to the desired inputs pass their values unmodified, while those for the inputs that are to be ignored return a constant value. Another approach, for cases in which the desired inputs pertain to a subset of Components in the Composition - that solely responsible for determining its `net_outcome `, is to assign those + solely responsible for determining its `net_outcome `, is to assign those Components to a `nested Composition ` and assign that Composition as the `agent_rep - `. A third, more sophisticated approach, would be to assign + `. A third, more sophisticated approach, is to assign ControlSignals to the InputPorts for the irrelevant features, and specify them to suppress their values. .. _OptimizationControlMechanism_Agent_Rep_CFA: @@ -288,62 +292,111 @@ .. _OptimizationControlMechanism_State_Features_Shadow_Inputs: The specifications in the **state_features** argument are used to construct the `state_input_ports - `, and can be any of the following, used either singly or in a list: - - .. _Optimization_Control_Mechanism_Input_Port_State_Feature: - * *InputPort specification* -- this creates an `InputPort` as one of the OptimizationControlMechanism's - `state_input_ports ` that `shadows ` the - input to the specified InputPort; that is, the value of which is used as the corresponding value of the - OptimizationControlMechanism's `state_feature_values `. + `. As noted + `above `, specifying these explicitly overrides + their automatic construction. They can be specified using any of the following: + + .. _Optimization_Control_Mechanism_State_Feature_Input_Dict: + + * *Inputs dictionary* -- a dictionary that conforms to the format used to `specify inputs + ` to the `agent_rep `, in which entries + consist of a key specifying an `INPUT ` Node of `agent_rep + `, and its value is the source of the input, that can be any of the forms + of individual input specifications listed `below `. + The full format required for inputs to `agent_rep ` can be seen using + its `get_input_format ` method. If only some `INPUT ` Nodes are + specified, the remaining ones are assigned their `default values ` when the `agent_rep + `\\'s `evaluate ` method is called. + This is the most reliable and straightforward way to specify **state_features**. + + .. _Optimization_Control_Mechanism_State_Feature_List_Inputs: + + * *List* -- a list of individual input source specifications, that can be any of the forms of individual input + specifications listed `below `. These are assumed + to be listed in the order that `INPUT ` Nodes are listed in the of the `nodes ` + attribute of `agent_rep ` (and returned by a call to its + `get_nodes_by_role(NodeRole.INPUT) ` method). If the list is incomplete, + the remaining ones are assigned their `default values ` when the `agent_rep + `\\'s `evaluate ` method is called. + + .. _Optimization_Control_Mechanism_State_Feature_Individual_Inputs: + + * *Individual inputs* -- any of the forms below can be used singly, or in a dict or list as described + `above `, to configure a `state_input_port + `, the value of which is assigned as the corresponding element + of `state_feature_values ` provided as input to the `INPUT + ` `Node ` of the `agent_rep ` when it + is `evaluated ` method is called. .. note:: - Only the `INPUT ` `Nodes ` of a `nested Composition ` - can shadowed. Therefore, if the Composition that an OptimizationControlMechanism controls contains any - nested Compositions, only its `INPUT ` Nodes can be specified for shadowing in the - **state_features** argument of the OptimizationControlMechanism's constructor. - - .. hint:: - Shadowing the input to a Node of a `nested Composition ` that is not an `INTERNAL - ` Node of that Composition can be accomplished one or of two ways, by: a) assigning it - `INPUT ` as a `required NodeRole ` where it is added to - the nested Composition; and/or b) adding an additional Node to that Composition that shadows the desired one - (this is allowed *within* the *same* Composition), and is assigned as an `OUTPUT ` Node of - that Composition, the `OutputPort` of which which can then be specified in the **state_features** argument of - the OptimizationControlMechanism's constructor (see below). - - .. technical_note:: - The InputPorts specified as state_features are marked as `internal_only ` = `True`. - - .. _Optimization_Control_Mechanism_Output_Port_State_Feature: - * *OutputPort specification* -- this can be any form of `OutputPort specification ` - for any `OutputPort` of another `Mechanism ` in the Composition; the `value ` - of the specified OutputPort is used as the corresponding value of the OptimizationControlMechanism's - `state_feature_values `. - - .. _Optimization_Control_Mechanism_Mechanism_State_Feature: - * *Mechanism* -- if the `agent_rep ` is a Composition, the Mechanism must - be an `INPUT ` `Node ` of that Composition, and the Mechanism's `primary - InputPort ` is `shadowed ` (since in - this case the state_feature must correspond to an input to the Composition). If the Mechanism is not an `INPUT - ` Node, an error is generated; if its OutputPort is to be used, that needs to be specified - explicitly (as described `above `). In contrast, if the - `agent_rep ` is a `CompositionFunctionApproximator, then the Mechanism's - `primary OutputPort ` *is* used (since that is the typical usage for specifying an `InputPort - `); if the input to the Mechanism is to be shadowed, then its InputPort must be - specified explicitly (as described `above `). - - * *Mechanism* -- if the `agent_rep ` is a Composition, the Mechanism's - `primary InputPort ` is shadowed in same way as if it had been explicit `input_port - specification `. If the Mechanism is in a `nested - Composition `, it must be an `INPUT ` `Node ` of that - Composition (see note above); if its OutputPort needs to be used, it must be specified explicitly (as described - `above `). In contrast, if the `agent_rep - ` is a `CompositionFunctionApproximator`, then the Mechanism's - `primary OutputPort ` *is* used (since that is typical usage, and there are no assumptions - made about the state features of a `CompositionFunctionApproximator`); if the input to the Mechanism *is* to be - shadowed, then its InputPort must be specified explicitly (as described `above - `). - + If only a single input specification is provided to **state_features**, it is treated as a list with a single + item (see `above `), and assigned as the input to the + first `INPUT ` Node of `agent_rep `; if the latter has + any additional `INPUT ` Nodes, they are assigned their `default values `. + + .. _Optimization_Control_Mechanism_Numeric_State_Feature: + * *numeric value* -- create an `InputPort` with the specified value as its `default value ` + and no `afferent Projections `; as a result, the specified value is assigned as the + input to the corresponding `INPUT ` `Node ` of the `agent_rep + ` each time it is `evaluated `. + + .. _Optimization_Control_Mechanism_Tuple_State_Feature: + * *2-item tuple* -- the first item must be a `Port` or `Mechanism` specification, as described below; the + second item must be a `Function` that is assigned as the `function ` of the corresponding + `state_input_port ` (see `state feature functions + ` for additional details). + + .. _Optimization_Control_Mechanism_Tuple_State_Feature: + * *specification dictionary* -- an `InputPort specification dictionary ` can be + used to configure the corresponding `state_input_port `, if + `Parameters ` other than its `function ` need to be specified (e.g., its `name + ` or more than a single `afferent Projection `). + + .. _Optimization_Control_Mechanism_Input_Port_State_Feature: + * *InputPort specification* -- create an `InputPort` that `shadows ` the + input to the specified InputPort, ,the value of which is used as the corresponding value of the + OptimizationControlMechanism's `state_feature_values `. + + .. note:: + Only the `INPUT ` `Nodes ` of a `nested Composition ` + can shadowed. Therefore, if the Composition that an OptimizationControlMechanism controls contains any + nested Compositions, only its `INPUT ` Nodes can be specified for shadowing in the + **state_features** argument of the OptimizationControlMechanism's constructor. + + .. hint:: + Shadowing the input to a Node of a `nested Composition ` that is not an `INTERNAL + ` Node of that Composition can be accomplished one or of two ways, by: a) assigning it + `INPUT ` as a `required NodeRole ` where it is added to + the nested Composition; and/or b) adding an additional Node to that Composition that shadows the desired one + (this is allowed *within* the *same* Composition), and is assigned as an `OUTPUT ` Node of + that Composition, the `OutputPort` of which which can then be specified in the **state_features** argument of + the OptimizationControlMechanism's constructor (see below). + + .. technical_note:: + The InputPorts specified as state_features are marked as `internal_only ` = `True`. + + .. _Optimization_Control_Mechanism_Output_Port_State_Feature: + * *OutputPort specification* -- this creates an `InputPort` that receives a `MappingProjection` from the + specified `OutputPort`; it can be any form of `OutputPort specification ` + for any `OutputPort` of another `Mechanism ` in the Composition. The `value ` + of the specified OutputPort is used as the corresponding value of the OptimizationControlMechanism's + `state_feature_values `. + + .. _Optimization_Control_Mechanism_Mechanism_State_Feature: + + * *Mechanism* -- if the `agent_rep ` is a Composition, the Mechanism's + `primary InputPort ` is shadowed; that is, it is assumed that its' input should be used + as the corresponding value of the OptimizationControlMechanism's `state_feature_values + `. This has the same result as explicitly specifying the + Mechanism's input_port, as described `above `. If + the Mechanism is in a `nested Composition `, it must be an `INPUT ` `Node + ` of that Composition (see note above). If its OutputPort needs to be used, it must be + specified explicitly (as described `above `). In + contrast, if the `agent_rep ` is a `CompositionFunctionApproximator`, + then the Mechanism's `primary OutputPort ` is used (since that is typical usage, and there + are no assumptions made about the state features of a `CompositionFunctionApproximator`); if the input to the + Mechanism *is* to be shadowed, then its InputPort must be specified explicitly (as described `above + `). COMMENT: FIX: CONFIRM THAT THE FOLLOWING ALL WORK @@ -570,7 +623,7 @@ ` are distinct from, and should not be confused with the `objective_function ` parameter of the OptimizationControlMechanism's `function `. The `objective_mechanism `\\'s - `function ` evaluates the `outcome ` of processing + `function ` evaluates the `outcome ` of processing without taking into account the `costs ` of the OptimizationControlMechanism's `control_signals `. In contrast, its `evaluate_agent_rep ` method, which is assigned as the `objective_function` @@ -612,7 +665,7 @@ `value ` is not included in the `output_values ` or `results ` attributes of the Composition for which the OptimizationControlMechanism is the `controller `, unless that Composition's `include_probes_in_output - ` attribute is set to True (see `Composition_Probes` for additional + ` attribute is set to True (see Probes `Composition_Probes` for additional information). .. _OptimizationControlMechanism_Function: @@ -872,7 +925,7 @@ import typecheck as tc from psyneulink.core import llvm as pnlvm -from psyneulink.core.components.component import DefaultsFlexibility +from psyneulink.core.components.component import DefaultsFlexibility, Component from psyneulink.core.components.functions.function import is_function_type from psyneulink.core.components.functions.nonstateful.optimizationfunctions import \ GridSearch, OBJECTIVE_FUNCTION, SEARCH_SPACE, RANDOMIZATION_DIMENSION @@ -883,19 +936,19 @@ from psyneulink.core.components.ports.inputport import InputPort, _parse_shadow_inputs from psyneulink.core.components.ports.modulatorysignals.controlsignal import ControlSignal from psyneulink.core.components.ports.outputport import OutputPort -from psyneulink.core.components.ports.port import _parse_port_spec, _instantiate_port +from psyneulink.core.components.ports.port import _parse_port_spec, _instantiate_port, Port from psyneulink.core.components.shellclasses import Function from psyneulink.core.globals.context import Context, ContextFlags from psyneulink.core.globals.context import handle_external_context from psyneulink.core.globals.defaults import defaultControlAllocation from psyneulink.core.globals.keywords import \ - ALL, COMPOSITION, COMPOSITION_FUNCTION_APPROXIMATOR, CONCATENATE, DEFAULT_VARIABLE, EID_FROZEN, \ + ALL, COMPOSITION, COMPOSITION_FUNCTION_APPROXIMATOR, CONCATENATE, DEFAULT_INPUT, DEFAULT_VARIABLE, EID_FROZEN, \ FUNCTION, INTERNAL_ONLY, NAME, OPTIMIZATION_CONTROL_MECHANISM, OWNER_VALUE, PARAMS, PROJECTIONS, \ - SHADOW_INPUTS, SHADOW_INPUT_NAME + SHADOW_INPUTS, SHADOW_INPUT_NAME, VALUE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.sampleiterator import SampleIterator, SampleSpec -from psyneulink.core.globals.utilities import convert_to_list, convert_to_np_array, ContentAddressableList +from psyneulink.core.globals.utilities import convert_to_list, convert_to_np_array, ContentAddressableList, is_numeric from psyneulink.core.llvm.debug import debug_env __all__ = [ @@ -960,12 +1013,11 @@ class OptimizationControlMechanism(ControlMechanism): --------- state_features : Mechanism, InputPort, OutputPort, Projection, dict, or list containing any of these - specifies Components for which `state_input_ports ` - are created, the `values ` of which are assigned to `state_feature_values - ` and used to predict `net_outcome - `. Any `InputPort specification ` - can be used that resolves to an `OutputPort` that projects to that InputPort (see - `state_features ` for additional details). + specifies the Components from which `state_input_ports ` + receive their inputs, the `values ` of which are assigned to `state_feature_values + ` and provided as input to the `agent_rep + 's `evaluate ` method. See `state_features + ` for details of specification. state_feature_functions : Function or function : default None specifies the `function ` assigned the `InputPort` in `state_input_ports @@ -1063,22 +1115,28 @@ class OptimizationControlMechanism(ControlMechanism): one of its subclasses, or it has not been assigned (None) (see `Agent Representation and Types of Optimization ` for additional details). - state_features : List[Mechanism, InputPort, or OutputPort, Projection, or dict] - lists the specifications provided to the **state_features** argument of the OptimizationControlMechanism's - constructor, that are used to generate the inputs to `state_input_ports - ` (see `OptimizationControlMechanism_State_Features` for - additional details). + state_features : Dict[Node:Port] + dictionary listing the `INPUT ` `Nodes ` of `agent_rep + ` (keys) and the specifications in the **state_features** + argument (values) of the sources of their inputs used to construct `state_input_ports + `, the values of which are assigned to + `state_feature_values ` and provided as + input to the `agent_rep 's `evaluate ` + method when it is executed (see `state_features ` + and `OptimizationControlMechanism_State_Features` for additional details). state_feature_values : 2d array - the current value of each item of the OptimizationControlMechanism's - `OptimizationControlMechanism_State_Features` (each of which is a 1d array). + the current value of each item of the OptimizationControlMechanism's `state_input_ports + (see `OptimizationControlMechanism_State_Features` for + additional details). state_input_ports : ContentAddressableList lists the OptimizationControlMechanism's `InputPorts ` that receive `Projections ` - from the items specified in the **state_features** argument in the OptimizationControlMechanism's constructor - or constructed automatically (see `state_features `), and - that provide the `state_feature_values ` to the `agent_rep - ` (see `OptimizationControlMechanism_State_Features` for additional details). + from the items specified in the **state_features** argument in the OptimizationControlMechanism's constructor, + or constructed automatically (see `state_features `), the + values of which are assigned to `state_feature_values ` + and provided as input to the `agent_rep 's `evaluate + ` method (see `OptimizationControlMechanism_State_Features` for additional details). num_state_input_ports : int cantains the number of `state_input_ports `. @@ -1417,7 +1475,8 @@ def __init__(self, state_feature_functions = kwargs['feature_function'] kwargs.pop('feature_function') continue - self.state_features = convert_to_list(state_features) + + self.state_features = state_features if isinstance(state_features, dict) else convert_to_list(state_features) function = function or GridSearch @@ -1607,39 +1666,51 @@ def _update_state_input_ports_for_controller(self, context=None): # TRY TESTS WITHOUT THIS # Don't instantiate unless being called by Composition.run() (which does not use ContextFlags.METHOD) # This avoids error messages if called prematurely (i.e., before run is complete) - # MODIFIED 11/29/21 OLD: if context.flags & ContextFlags.METHOD: return - # MODIFIED 11/29/21 END - # Don't bother for model-free optimization (see OptimizationControlMechanism_Model_Free) - # since state_input_ports specified or model-free optimization are entirely the user's responsibility; - # this is because they can't be programmatically validated against the agent_rep's evaluate() method. - # (This contrast with model-based optimization, for which there must be a state_input_port for every - # InputPort of every INPUT node of the agent_rep (see OptimizationControlMechanism_Model_Based). + # Don't bother for agent_rep that is not a Composition, since state_input_ports specified can be validated + # or assigned by default for a CompositionApproximator, and so are either up to its implementation or to + # do the validation and/or default assignment (this contrasts with agent_rep that is a Composition, for + # which there must be a state_input_port for every InputPort of every INPUT node of the agent_rep. if self.agent_rep_type != COMPOSITION: return from psyneulink.core.compositions.composition import \ - Composition, NodeRole, CompositionInterfaceMechanism, CompositionError - - def _get_all_input_nodes(comp): - """Return all input_nodes, including those for any Composition nested one level down. - Note: more deeply nested Compositions will either be served by their containing one(s) or own controllers - """ - _input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) - input_nodes = [] - for node in _input_nodes: - if isinstance(node, Composition): - input_nodes.extend(_get_all_input_nodes(node)) - else: - input_nodes.append(node) - return input_nodes + Composition, CompositionInterfaceMechanism, CompositionError, RunError, NodeRole if self.state_features: # Validate state_features, and instantiate any that are not shadowing nodes # Shadowing nodes are instantiated in Composition._update_shadow_projections() comp = self.agent_rep + + if isinstance(self.state_features, list): + # Convert list to dict, assuming list is in order of INPUT Nodes, + # and assigning the corresponding INPUT Nodes as keys for use in comp._build_predicted_inputs_dict() + input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) + if len(self.state_features) > len(input_nodes): + raise OptimizationControlMechanismError( + f"The number of 'state_features' specified for {self.name} ({len(self.state_features)}) " + f"is more than the number of INPUT Nodes ({len(input_nodes)}) of the Composition assigned " + f"as its {AGENT_REP} ('{self.agent_rep.name}').") + input_dict = {} + for i, spec in enumerate(self.state_features): + input_dict[input_nodes[i]] = spec + self.state_features = input_dict + if isinstance(self.state_features, dict): + # If dict is specified, get values for checks below + state_features = list(self.state_features.values()) + assert isinstance(self.state_features, dict) + + # FIX: 1/14/21: MOVE ALL OF THIS TO ITS OWN METHOD: _validate_state_feature_specs: + + # Include agent rep in error messages if it is not the same as self.composition + self_has_state_features_str = f"'{self.name}' has 'state_features' specified " + agent_rep_str = ('' if self.agent_rep == self.composition + else f"both its `{AGENT_REP}` ('{self.agent_rep.name}') as well as ") + not_in_comps_str = f"that are missing from {agent_rep_str}'{self.composition.name}' and any " \ + f"{Composition.componentCategory}s nested within it." + # Ensure that all InputPorts shadowed by specified state_input_ports # are in agent_rep or one of its nested Compositions invalid_state_features = [input_port for input_port in self.state_input_ports @@ -1656,88 +1727,101 @@ def _get_all_input_nodes(comp): continue try: all(comp._get_source(p) for p in input_port.path_afferents) - # except CompositionError: except CompositionError: invalid_state_features.append(input_port) - if any(invalid_state_features): - raise OptimizationControlMechanismError(f"{self.name}, being used as controller for model-based " - f"optimization of {self.agent_rep.name}, has 'state_features' " - f"specified ({[d.name for d in invalid_state_features]}) that " - f"are missing from the Composition or any nested within it.") + raise OptimizationControlMechanismError( + self_has_state_features_str + f"({[d.name for d in invalid_state_features]}) " + not_in_comps_str) # Ensure that all InputPorts shadowed by specified state_input_ports # reference INPUT Nodes of agent_rep or of a nested Composition invalid_state_features = [input_port for input_port in self.state_input_ports if (input_port.shadow_inputs and not (input_port.shadow_inputs.owner - in _get_all_input_nodes(self.agent_rep)) + in self._get_agent_rep_input_nodes()) and (isinstance(input_port.shadow_inputs.owner, CompositionInterfaceMechanism) and not (input_port.shadow_inputs.owner.composition in [nested_comp for nested_comp in comp._get_nested_compositions() if nested_comp in comp.get_nodes_by_role(NodeRole.INPUT)])))] if any(invalid_state_features): - raise OptimizationControlMechanismError(f"{self.name}, being used as controller for model-based " - f"optimization of {self.agent_rep.name}, has 'state_features' " - f"specified ({[d.name for d in invalid_state_features]}) that " - f"are not INPUT nodes for the Composition or any nested " - f"within it.") - # # MODIFIED 1/9/22 NEW: - # try: - # # Test whether state_features specified are compatible with inputs format required by agent_rep - # self.agent_rep._build_predicted_inputs_dict(None) - # except: - # raise OptimizationControlMechanismError( - # f"The 'state_features' argument has been specified for '{self.name}' that is using a " - # f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but the 'state_features' " - # f"({self.state_features}) specified are not compatible with the inputs required by 'agent_rep' " - # f"when it is executed. It's get_inputs_format() method can be used to see the format required; " - # f"You can also remove the specification of 'state_features' from the constructor for {self.name} " - # f"to allow their automatic assignment.") - # MODIFIED 1/9/22 END - - warnings.warn(f"The 'state_features' argument has been specified for '{self.name}', that is being " - f"configured to use a {Composition.componentType} ('{self.agent_rep.name}') as its " - f"'{AGENT_REP}'). This overrides automatic assignment of its 'state_features' as inputs to " - f"'{self.agent_rep.name}' when it is executed. If they are not properly configured, it " - f"will cause an error. Remove this specification from the constructor for '{self.name}' to " - f"automatically configure its 'state_features' to be the external inputs to " - f"'{self.agent_rep.name}'.") + raise OptimizationControlMechanismError( + self_has_state_features_str + f"({[d.name for d in invalid_state_features]}) " + not_in_comps_str) + + try: + # Test if state_features specified are compatible with inputs format for agent_rep Composition + # FIX: 1/10/22 - ?USE self.agent_rep.external_input_values FOR CHECK? + # Note: if state_features is a dict, keys are used in _build_predicc_inputs to identify INPUT Nodes + inputs = self.agent_rep._build_predicted_inputs_dict(None, self) + inputs_dict, num_inputs = self.agent_rep._parse_input_dict(inputs) + if len(self.state_input_ports) < len(inputs_dict): + warnings.warn(f"The 'state_features' specified for '{self.name}' are legal, but there are fewer " + f"than the number of input_nodes for its {AGENT_REP} ('{self.agent_rep.name}'); " + f"the remaining inputs will be assigned default values. Use the {AGENT_REP}'s " + f"get_inputs_format() method to see the format for its inputs.") + except RunError as error: + raise OptimizationControlMechanismError( + f"The 'state_features' argument has been specified for '{self.name}' that is using a " + f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but " + f"they are not compatible with the inputs required by its 'agent_rep': '{error.error_value}' " + f"Use the get_inputs_format() method of '{self.agent_rep.name}' to see the required format, or " + f"remove the specification of 'state_features' from the constructor for {self.name} " + f"to have them automatically assigned.") + except KeyError as error: # This occurs if a Node is illegal for a reason other than above, + pass # and will issue the corresponding error message. + except: # Legal Node specifications, but incorrect for input to agent_rep + specs = [f.full_name if hasattr(f, 'full_name') else (f.name if isinstance(f, Component) else f) + for f in state_features] + raise OptimizationControlMechanismError( + f"The 'state_features' argument has been specified for '{self.name}' that is using a " + f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but the " + f"'state_features' ({specs}) specified are not compatible with the inputs required by 'agent_rep' " + f"when it is executed. Use its get_inputs_format() method to see the required format, " + f"or remove the specification of 'state_features' from the constructor for {self.name} " + f"to have them automatically assigned.") + + # warnings.warn(f"The 'state_features' argument has been specified for '{self.name}', that is being " + # f"configured to use a {Composition.componentType} ('{self.agent_rep.name}') as its " + # f"'{AGENT_REP}'). This overrides automatic assignment of its 'state_features' as inputs to " + # f"'{self.agent_rep.name}' when it is executed. If they are not properly configured, it " + # f"will cause an error. Remove this specification from the constructor for '{self.name}' to " + # f"automatically configure its 'state_features' to be the external inputs to " + # f"'{self.agent_rep.name}'.") return - # agent_rep is Composition, but no state_features have been specified, - # so assign a state_input_port to shadow every InputPort of every INPUT node of agent_rep - shadow_input_ports = [] - for node in _get_all_input_nodes(self.agent_rep): - for input_port in node.input_ports: - if input_port.internal_only: - continue - # if isinstance(input_port.owner, CompositionInterfaceMechanism): - # input_port = input_port. - shadow_input_ports.append(input_port) - - local_context = Context(source=ContextFlags.METHOD) - state_input_ports_to_add = [] - # for input_port in input_ports_not_specified: - for input_port in shadow_input_ports: - input_port_name = f"{SHADOW_INPUT_NAME} of {input_port.owner.name}[{input_port.name}]" - params = {SHADOW_INPUTS: input_port, - INTERNAL_ONLY:True} - # Note: state_feature_functions has been validated _validate_params - # to have only a single function in for model-based agent_rep - if self.state_feature_functions: - params.update({FUNCTION: self._parse_state_feature_function(self.state_feature_functions)}) - state_input_ports_to_add.append(_instantiate_port(name=input_port_name, - port_type=InputPort, - owner=self, - reference_value=input_port.value, - params=params, - context=local_context)) - self.add_ports(state_input_ports_to_add, - update_variable=False, - context=local_context) - self.state_input_ports.extend(state_input_ports_to_add) + else: + # agent_rep is Composition, but no state_features have been specified, + # so assign a state_input_port to shadow every InputPort of every INPUT node of agent_rep + shadow_input_ports = [] + for node in self._get_agent_rep_input_nodes(): + for input_port in node.input_ports: + if input_port.internal_only: + continue + # if isinstance(input_port.owner, CompositionInterfaceMechanism): + # input_port = input_port. + shadow_input_ports.append(input_port) + + local_context = Context(source=ContextFlags.METHOD) + state_input_ports_to_add = [] + # for input_port in input_ports_not_specified: + for input_port in shadow_input_ports: + input_port_name = f"{SHADOW_INPUT_NAME} of {input_port.owner.name}[{input_port.name}]" + params = {SHADOW_INPUTS: input_port, + INTERNAL_ONLY:True} + # Note: state_feature_functions has been validated _validate_params + # to have only a single function in for model-based agent_rep + if self.state_feature_functions: + params.update({FUNCTION: self._parse_state_feature_function(self.state_feature_functions)}) + state_input_ports_to_add.append(_instantiate_port(name=input_port_name, + port_type=InputPort, + owner=self, + reference_value=input_port.value, + params=params, + context=local_context)) + self.add_ports(state_input_ports_to_add, + update_variable=False, + context=local_context) + self.state_input_ports.extend(state_input_ports_to_add) def _instantiate_output_ports(self, context=None): """Assign CostFunctions.DEFAULTS as default for cost_option of ControlSignals. @@ -2406,6 +2490,24 @@ def agent_rep_type(self): else: return None + def _get_agent_rep_input_nodes(self, comp=None): + """Return all input_nodes of agent_rep, including those for any Composition nested one level down. + Note: more deeply nested Compositions will either be served by their containing one(s) or own controllers + """ + from psyneulink.core.compositions.composition import Composition, NodeRole + if not self.agent_rep_type: + return [None] + comp = comp or self.agent_rep + _input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) + input_nodes = [] + for node in _input_nodes: + if isinstance(node, Composition): + input_nodes.extend(self._get_agent_rep_input_nodes(node)) + else: + input_nodes.append(node) + return input_nodes + + def _parse_state_feature_function(self, feature_function): if isinstance(feature_function, Function): return copy.deepcopy(feature_function) @@ -2415,18 +2517,31 @@ def _parse_state_feature_function(self, feature_function): @tc.typecheck def _parse_state_feature_specs(self, state_features, feature_functions, context=None): """Parse entries of state_features into InputPort spec dictionaries - Set INTERNAL_ONLY entry of params dict of InputPort spec dictionary to True - (so that inputs to Composition are not required if the specified state is on an INPUT Mechanism) + For entries specifying shadowing, set INTERNAL_ONLY entry of params dict for InputPort spec dictionary to True + (so that inputs to Composition are not required if the specified state feature is on an INPUT Mechanism) Assign functions specified in **state_feature_functions** to InputPorts for all state_features - Return list of InputPort specification dictionaries + Return list of InputPort specification dictionaries for state_input_ports """ + input_node_names = [n.name if n else None for n in self._get_agent_rep_input_nodes()] + input_port_names = None + if isinstance(state_features, dict): + if SHADOW_INPUTS in self.state_features: + pass # handled below + else: + input_port_names = [k.name for k in list(state_features.keys())] + state_features = list(state_features.values()) _state_input_ports = _parse_shadow_inputs(self, state_features) parsed_features = [] - for spec in _state_input_ports: - # MODIFIED 11/29/21 NEW: + for i, spec in enumerate(_state_input_ports): + # If spec is numeric, assign as default value and InputPort function that simply returns that value + if is_numeric(spec): + spec_val = copy.copy(spec) + spec = {VALUE: spec_val, + PARAMS: {DEFAULT_INPUT: DEFAULT_VARIABLE} + } # If optimization uses Composition, assume that shadowing a Mechanism means shadowing its primary InputPort if isinstance(spec, Mechanism): if self.agent_rep_type == COMPOSITION: @@ -2441,9 +2556,18 @@ def _parse_state_feature_specs(self, state_features, feature_functions, context= else: spec = spec.output_port parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) - if not parsed_spec[NAME]: - parsed_spec[NAME] = spec.full_name - if SHADOW_INPUTS in parsed_spec[PARAMS]: + + if input_port_names: + # Use keys from input dict as names of state_input_ports + # (needed by comp._build_predicted_inputs_dict to identify INPUT nodes) + parsed_spec[NAME] = input_port_names[i] + elif not parsed_spec[NAME]: + if isinstance(spec, Port): + parsed_spec[NAME] = spec.full_name + else: + # Assumes specs specified in order of agent_rep's INPUT Nodes + parsed_spec[NAME] = input_node_names[i] + if parsed_spec[PARAMS] and SHADOW_INPUTS in parsed_spec[PARAMS]: # Composition._update_shadow_projections will take care of PROJECTIONS specification parsed_spec[PARAMS].update({INTERNAL_ONLY:True, PROJECTIONS:None}) @@ -2470,7 +2594,8 @@ def num_state_input_ports(self): def state(self): state_feature_values = (self.state_feature_values if len(self.state_feature_values) else self.state_input_ports.values) - return np.append(state_feature_values, self.control_allocation, 0) + # return np.append(state_feature_values, self.control_allocation, 0) + return [v.tolist() for v in state_feature_values] + self.control_allocation.tolist() # FIX: 1/6/22 - FINISH IMPLEMENTING: # - ADD ENTRIES FOR ALL NODES THAT CONTRIBUTE TO STATE_INPUT_PORTS, EVEN IF CONVERGENT @@ -2500,9 +2625,17 @@ def state_dict(self): else: composition = port.path_afferents[0].sender.owner.composition get_info_method = composition._get_destination - assert port.path_afferents, f"PROGRAM ERROR: state_input_port {state_index} ('{port.name}')" \ - f"for {self.name} does not have any Projections to it" - source_port, node, comp = get_info_method(port.path_afferents[0]) + if not port.path_afferents: + if port.default_input is DEFAULT_VARIABLE: + # FIX: 1/14/22 DOUBLECHECK THAT THE FOLLWING IS CORRECT: + source_port = DEFAULT_VARIABLE + node = None + comp = None + else: + assert port.path_afferents, f"PROGRAM ERROR: state_input_port {state_index} ('{port.name}')" \ + f"for {self.name} does not have any Projections to it" + else: + source_port, node, comp = get_info_method(port.path_afferents[0]) state_dict.update({(source_port, node, comp, state_index):self.state[state_index]}) # # MODIFIED 1/8/22 ALT: SEPARATELY LISTS OUTPUT_PORTS THAT PROJECT TO SAME SHADOWED INPUT_PORT # if port.shadow_inputs: diff --git a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py index 5a65c562c4a..80869f28701 100644 --- a/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py +++ b/psyneulink/core/components/mechanisms/processing/compositioninterfacemechanism.py @@ -35,7 +35,7 @@ `compilation `. By providing the standard Components for communication among `Mechanisms ` (`InputPorts ` and `OutputPorts `), Mechanisms (and/or other Compositions) that are `INPUT ` `Nodes ` of a Composition can receive inputs from the environment - in the same way that any other Node receives inputs, from `afferent Projections ` (in + in the same way that any other Node receives inputs, from `afferent Projections ` (in this case, the `input_CIM ` of the Composition to which they belong); and, similarly, Components that are `OUTPUT ` `Nodes ` of a Composition can either report their outputs to the Composition or, if they are in a `nested Composition `, send their outputs to diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index bfb01dd4285..1479dfb9b25 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -209,7 +209,7 @@ * **InputPort specification dictionary** -- this can be used to specify the attributes of an InputPort, using any of the entries that can be included in a `Port specification dictionary ` (see `examples ` in Port). If the dictionary is used to specify an - InputPort in the constructor for a Mechanism, and it includes a *VARIABLE* and/or *VALUE* or entry, the value + InputPort in the constructor for a Mechanism, and it includes a *VARIABLE* and/or *VALUE* entry, the value must be compatible with the item of the owner Mechanism's `variable ` to which the InputPort is assigned (see `Mechanism InputPort specification `). @@ -439,18 +439,18 @@ is the `ORIGIN` Mechanism for that Process or System). It has the following attributes, that includes ones specific to, and that can be used to customize the InputPort: -* `projections ` -- all of the `Projections ` received by the InputPort. +* `projections ` -- all of the `Projections ` received by the InputPort. .. _InputPort_Afferent_Projections: -* `path_afferents ` -- `MappingProjections ` that project to the InputPort, +* `path_afferents ` -- `MappingProjections ` that project to the InputPort, the `value `\\s of which are combined by the InputPort's `function `, possibly modified by its `mod_afferents `, and assigned to the corresponding item of the owner Mechanism's `variable `. -* `mod_afferents ` -- `GatingProjections ` that project to the InputPort, - the `value ` of which can modify the InputPort's `value ` (see the - descriptions of Modulation under `ModulatorySignals ` and `GatingSignals +* `mod_afferents ` -- `GatingProjections ` that project to the + InputPort, the `value ` of which can modify the InputPort's `value ` + (see the descriptions of Modulation under `ModulatorySignals ` and `GatingSignals ` for additional details). If the InputPort receives more than one GatingProjection, their values are combined before they are used to modify the `value ` of InputPort. @@ -458,10 +458,14 @@ * `variable ` -- serves as the template for the `value ` of the `Projections ` received by the InputPort: each must be compatible with (that is, match both the - number and type of elements of) the InputPort's `variable ` (see `Mapping_Matrix` for additonal - details). In general, this must also be compatible with the item of the owner Mechanism's `variable + number and type of elements of) the InputPort's `variable ` (see `matrix ` + for additional details). In general, this must also be compatible with the item of the owner Mechanism's `variable ` to which the InputPort is assigned (see `above ` and - `Mechanism InputPort specification `). + `Mechanism InputPort specification `). The InputPort's `variable + ` is composed of the concatenated `values ` of its `path_afferent + ` Projections. If it has none, then the `variable ` is either assigned + None or its `default value `, as determined by its `default_input ` + setting. .. _InputPort_Function: @@ -498,12 +502,16 @@ An InputPort cannot be executed directly. It is executed when the Mechanism to which it belongs is executed. When this occurs, the InputPort executes any `Projections ` it receives, calls its `function ` to combines the values received from any `MappingProjections ` it receives -(listed in its its `path_afferents ` attribute) and modulate them in response to any -`GatingProjections ` (listed in its `mod_afferents ` attribute), +(listed in its its `path_afferents ` attribute) and modulate them in response to any +`GatingProjections ` (listed in its `mod_afferents ` attribute), and then assigns the result to the InputPort's `value ` attribute. This, in turn, is assigned to the item of the Mechanism's `variable ` and `input_values ` -attributes corresponding to that InputPort (see `Mechanism Variable and InputPorts -` for additional details). +attributes corresponding to that InputPort (see `Mechanism Variable and InputPorts ` +for additional details). If an InputPort does not have any `path_afferent Projections `, +by default its value is set to None which, when executed, generates an error; however, if its `default_input +` attribute is *DEFAULT_VARIABLE*, then the `default value ` for the +InputPort's `variable ` is used as its value (see `default_input ` for +additional details). .. _InputPort_Class_Reference: @@ -526,7 +534,8 @@ from psyneulink.core.components.ports.port import PortError, Port_Base, _instantiate_port_list, port_type_keywords from psyneulink.core.globals.context import ContextFlags, handle_external_context from psyneulink.core.globals.keywords import \ - COMBINE, CONTROL_SIGNAL, EXPONENT, FUNCTION, GATING_SIGNAL, INPUT_PORT, INPUT_PORTS, INPUT_PORT_PARAMS, \ + COMBINE, CONTROL_SIGNAL, DEFAULT_VARIABLE, EXPONENT, FUNCTION, GATING_SIGNAL, \ + INPUT_PORT, INPUT_PORTS, INPUT_PORT_PARAMS, \ LEARNING_SIGNAL, MAPPING_PROJECTION, MATRIX, NAME, OPERATION, OUTPUT_PORT, OUTPUT_PORTS, OWNER, \ PARAMS, PRODUCT, PROJECTIONS, REFERENCE_VALUE, \ SENDER, SHADOW_INPUTS, SHADOW_INPUT_NAME, SIZE, PORT_TYPE, SUM, VALUE, VARIABLE, WEIGHT @@ -593,10 +602,16 @@ class InputPort(Port_Base): the value of the item of the owner Mechanism's `variable ` attribute to which the InputPort is assigned; used as the template for the InputPort's `value ` attribute. + default_input : None or DEFAULT_VARIABLE : default None + specifies value to use as variable if the InputPort has no `path_afferent ` + `Projections `. If None (the default), then None is returned; otherwise the `default value + ` for the InputPort's `variable ` is used (see `default_input + ` for additional details). + variable : number, list or np.ndarray specifies the shape of the InputPort's `variable `, which may be used to define the shape of the `matrix ` parameter of the `MappingProjection` that projects to the - Inputport (see `InputPort_Variable` for additional details). + Inputport (see `variable ` for additional details). function : Function or method : default LinearCombination(operation=SUM) specifies the function applied to the variable. The default value combines the `values @@ -607,16 +622,16 @@ class InputPort(Port_Base): receives more than one Projection (see `function `. combine : SUM or PRODUCT : default None - specifies the **operation** argument used by the default `LinearCombination` function, which determines how the - `value ` of the InputPort's `projections ` are combined. This is a - convenience argument, that allows the **operation** to be specified without having to specify the + specifies the **operation** argument used by the default `LinearCombination` function, which determines how + the `value ` of the InputPort's `projections ` are combined. + This is a convenience argument, that allows the **operation** to be specified without having to specify the LinearCombination function; it assumes that LinearCombination (the default) is used as the InputPort's function -- if it conflicts with a specification of **function** an error is generated. projections : list of Projection specifications specifies the `MappingProjection(s) `, `ControlProjection(s) ` and/or `GatingProjection(s) ` to be received by the InputPort, and that are listed in its - `path_afferents ` and `mod_afferents ` attributes, + `path_afferents ` and `mod_afferents ` attributes, respectively (see `InputPort_Compatability_and_Constraints` for additional details). If **projections** but neither **variable** nor **size** are specified, then the `value ` of the Projection(s) or their `senders ` specified in **projections** argument are used to determine the @@ -642,20 +657,38 @@ class InputPort(Port_Base): **projections** is specified, then `variable ` is assigned the `value ` of the Projection(s) or its `sender `. + default_input : None or DEFAULT_VARIABLE + determines the value used as `variable ` if the InputPort has no `path_afferent + ` `Projections `. If None (the default), then None is used. This is the + default behavior, as it is useful for identifying "orphaned" InputPorts (i.e., ones that do not receive + any inputs) and the `Mechanisms ` to which they belong, as an error is returned if an InputPort + is executed and its variable is assigned None. If *default_input* is assigned *DEFAULT_VARIABLE*, then the + `default value ` for the InputPort's `variable ` is used as its value. + This is useful for assignment to a Mechanism that needs a fixed value as the input to its `function + `. + + .. note:: + If `default_input ` is assigned *DEFAULT_VARIABLE*, then its `internal_only + ` attribute is automtically assigned True. This is so that if the `Mechanism` + to which the InputPort belongs is assigned to a `Composition`, it is not treated as an `ORIGIN + ` `Node ` of that Composition (and automatically assigned a Projection + from its `input_CIM `. + function : Function If it is a `CombinationFunction`, it combines the `values ` of the `PathwayProjections ` (e.g., `MappingProjections `) received by the InputPort (listed in - its `path_afferents ` attribute), under the possible influence of `GatingProjections - ` received by the InputPort (listed in its `mod_afferents ` attribute). - The result is assigned to the InputPort's `value ` attribute. For example, the default - (`LinearCombination` with *SUM* as it **operation**) performs an element-wise (Hadamard) sum of its Projection - `values `, and assigns to `value ` an array that is of the same length - as each of the Projection `values `. If the InputPort receives only one Projection, - then any other function can be applied and it will generate a value that is the same length as the Projection's - `value `. However, if the InputPort receives more than one Projection and uses a function - other than a CombinationFunction, a warning is generated and only the `value ` of the - first Projection list in `path_afferents ` is used by the function, which may generate - unexpected results when executing the Mechanism or Composition to which it belongs. + its `path_afferents ` attribute), under the possible influence of `GatingProjections + ` received by the InputPort (listed in its `mod_afferents ` + attribute). The result is assigned to the InputPort's `value ` attribute. For example, the + the default (`LinearCombination` with *SUM* as it **operation**) performs an element-wise (Hadamard) sum of its + Projection `values `, and assigns to `value ` an array that is of the + same length as each of the Projection `values `. If the InputPort receives only one + Projection, then any other function can be applied and it will generate a value that is the same length as the + Projection's `value `. However, if the InputPort receives more than one Projection and + uses a function other than a CombinationFunction, a warning is generated and only the `value + ` of the first Projection list in `path_afferents ` is used + by the function, which may generate unexpected results when executing the Mechanism or Composition to which it + belongs. value : value or ndarray the output of the InputPort's `function `, that is assigned to an item of the owner @@ -673,9 +706,9 @@ class InputPort(Port_Base): see `weight and exponent ` for description. internal_only : bool - determines whether `input from a Composition ` must be specified for this - InputPort from a Composition's `execution method ` if the InputPort's `owner - ` is an `INPUT` `Node ` of that Composition; if `True`, external input is + determines whether `input from a Composition ` must be specified for this + InputPort from a Composition's `execution method ` if the InputPort's `owner + ` is an `INPUT` `Node ` of that Composition; if `True`, external input is *not* required or allowed. shadow_inputs : InputPort @@ -732,6 +765,12 @@ class Parameters(Port_Base.Parameters): :default value: None :type: + default_input + see 'default_input ` + + :default value: None + :type: None or DEFAULT_VARIABLE + exponent see `exponent ` @@ -764,6 +803,8 @@ class Parameters(Port_Base.Parameters): :default value: None :type: """ + default_input = Parameter(None, stateful=False, loggable=True, read_only=True, structural=True, + constructor_argument='default_input') function = Parameter(LinearCombination(operation=SUM), stateful=False, loggable=False) weight = Parameter(None, modulable=True) exponent = Parameter(None, modulable=True) @@ -771,6 +812,10 @@ class Parameters(Port_Base.Parameters): internal_only = Parameter(False, stateful=False, loggable=False, pnl_internal=True) shadow_inputs = Parameter(None, stateful=False, loggable=False, read_only=True, pnl_internal=True, structural=True) + def _validate_default_input(self, default_input): + if default_input not in {None, DEFAULT_VARIABLE}: + return f"must be None or the keyword '{DEFAULT_VARIABLE.upper()}'." + #endregion @handle_external_context() @@ -780,6 +825,7 @@ def __init__(self, reference_value=None, variable=None, size=None, + default_input=None, function=None, projections=None, combine:tc.optional(tc.enum(SUM,PRODUCT))=None, @@ -799,6 +845,9 @@ def __init__(self, if combine: self.combine_function_args = (combine, function) + if default_input == DEFAULT_VARIABLE: + internal_only = True + # If owner or reference_value has not been assigned, defer init to Port._instantiate_projection() # if owner is None or (variable is None and reference_value is None and projections is None): if owner is None: @@ -1233,7 +1282,11 @@ def _parse_self_port_type_spec(self, owner, input_port, context=None): f"with non-InputPort specification ({input_port}).") sender_output_ports = [p.sender for p in input_port.path_afferents] + # MODIFIED 1/12/22 OLD: port_spec = {NAME: SHADOW_INPUT_NAME + input_port.owner.name, + # # MODIFIED 1/12/22 NEW: + # port_spec = {NAME: SHADOW_INPUT_NAME + input_port.full_name, + # MODIFIED 1/12/22 END VARIABLE: np.zeros_like(input_port.variable), PORT_TYPE: InputPort, PROJECTIONS: sender_output_ports, @@ -1413,7 +1466,9 @@ def _instantiate_input_ports(owner, input_ports=None, reference_value=None, cont return port_list def _parse_shadow_inputs(owner, input_ports): - """Parses any {SHADOW_INPUTS:[InputPort or Mechanism,...]} items in input_ports into InputPort specif. dict.""" + """Parse any {SHADOW_INPUTS:[InputPort or Mechanism,...]} items in input_ports into InputPort specif. dict. + Return InputPort specification dict for any shadowing InputPorts, and unmodified spec for any others. + """ input_ports = convert_to_list(input_ports) input_ports_to_shadow_specs=[] diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index c481dda8d3c..bf5a834cce8 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -449,8 +449,7 @@ .. _port_value_Spec_Example: -For example, the following specifies the InputPort by a value to use as its `default_variable -` attribute:: +For example, the following specifies the InputPort by a value to use as its `variable ` attribute:: my_mech = pnl.TransferMechanism(input_ports=[[0,0]) @@ -787,9 +786,9 @@ def test_multiple_modulatory_projections_with_mech_and_port_Name_specs(self): from psyneulink.core.components.shellclasses import Mechanism, Projection, Port from psyneulink.core.globals.context import ContextFlags, handle_external_context from psyneulink.core.globals.keywords import \ - ADDITIVE, ADDITIVE_PARAM, AUTO_ASSIGN_MATRIX, \ - CONTEXT, CONTROL, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, DEFERRED_INITIALIZATION, DISABLE, EXPONENT, \ - FUNCTION, FUNCTION_PARAMS, GATING_PROJECTION_PARAMS, GATING_SIGNAL_SPECS, INPUT_PORTS, \ + ADDITIVE, ADDITIVE_PARAM, AUTO_ASSIGN_MATRIX, CONTEXT, CONTROL, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, \ + DEFAULT_INPUT, DEFAULT_VARIABLE, DEFERRED_INITIALIZATION, DISABLE,\ + EXPONENT, FUNCTION, FUNCTION_PARAMS, GATING_PROJECTION_PARAMS, GATING_SIGNAL_SPECS, INPUT_PORTS, \ LEARNING_PROJECTION_PARAMS, LEARNING_SIGNAL_SPECS, \ MATRIX, MECHANISM, MODULATORY_PROJECTION, MODULATORY_PROJECTIONS, MODULATORY_SIGNAL, \ MULTIPLICATIVE, MULTIPLICATIVE_PARAM, \ @@ -857,7 +856,7 @@ def __str__(self): return repr(self.error_value) -# DOCUMENT: INSTANTATION CREATES AN ATTIRBUTE ON THE OWNER MECHANISM WITH THE PORT'S NAME + VALUE_SUFFIX +# DOCUMENT: INSTANTIATION CREATES AN ATTIRBUTE ON THE OWNER MECHANISM WITH THE PORT'S NAME + VALUE_SUFFIX # THAT IS UPDATED BY THE PORT'S value setter METHOD (USED BY LOGGING OF MECHANISM ENTRIES) class Port_Base(Port): """ @@ -2121,6 +2120,8 @@ def _execute(self, variable=None, context=None, runtime_params=None): # return None, so that this port is ignored # KDM 8/2/19: double check the relevance of this branch if variable is None: + if hasattr(self, DEFAULT_INPUT) and self.default_input == DEFAULT_VARIABLE: + return self.defaults.variable return None return super()._execute( diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 6c8d44a6246..1c98b004d82 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2596,7 +2596,8 @@ def input_function(env, result): from psyneulink.core.components.functions.nonstateful.transferfunctions import Identity from psyneulink.core.components.mechanisms.mechanism import Mechanism_Base, MechanismError, MechanismList from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import ControlMechanism -from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP +from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP, \ + RANDOMIZATION_CONTROL_SIGNAL, NUM_ESTIMATES, STATE_FEATURES from psyneulink.core.components.mechanisms.modulatory.learning.learningmechanism import \ LearningMechanism, ACTIVATION_INPUT_INDEX, ACTIVATION_OUTPUT_INDEX, ERROR_SIGNAL, ERROR_SIGNAL_INDEX from psyneulink.core.components.mechanisms.modulatory.modulatorymechanism import ModulatoryMechanism_Base @@ -3056,8 +3057,9 @@ class NodeRole(enum.Enum): programmatically. INTERNAL - A `Node ` that is neither `ORIGIN` nor `TERMINAL`. This role cannot be modified - programmatically. + A `Node ` that is neither `INPUT` nor `OUTPUT`. Note that it *can* also be `ORIGIN`, + `TERMINAL` or `SINGLETON`, if it has no `afferent ` or `efferent + ` Projections or neither, respectively. This role cannot be modified programmatically. CYCLE A `Node ` that belongs to a cycle. This role cannot be modified programmatically. @@ -3858,11 +3860,16 @@ def add_node(self, node, required_roles=None, context=None): pathway_arg_str = " in " + context.string raise CompositionError(f"Attempt to add Composition as a Node to itself{pathway_arg_str}.") + required_roles = convert_to_list(required_roles) + if isinstance(node, Composition): # IMPLEMENTATION NOTE: include_probes_in_output=False is not currently supported for nested Nodes # (they require get_output_value() to return value of all output_ports of output_CIM) node.include_probes_in_output = True - + else: + if required_roles and NodeRole.INTERNAL in required_roles: + for input_port in node.input_ports: + input_port.internal_only = True try: node._analyze_graph(context = context) except AttributeError: @@ -4566,6 +4573,9 @@ def _determine_node_roles(self, context=None): # INPUT for node in self.get_nodes_by_role(NodeRole.ORIGIN): + # Don't allow INTERNAL Nodes to be INPUTS + if NodeRole.INTERNAL in self.get_roles_by_node(node): + continue self._add_node_role(node, NodeRole.INPUT) # CYCLE @@ -4974,6 +4984,7 @@ def _create_CIM_ports(self, context=None): variable=receiver.defaults.value, reference_value=receiver.defaults.value, name= PARAMETER_CIM_NAME + "_" + owner.name + "_" + receiver.name, + # default_input=DEFAULT_VARIABLE, context=context) self.parameter_CIM.add_ports([interface_input_port], context=context) # control signal for parameter CIM that will project directly to inner Composition's parameter @@ -5838,7 +5849,8 @@ def _check_for_projection_assignments(self, context=None): if context.source != ContextFlags.INITIALIZING and context.string != 'IGNORE_NO_AFFERENTS_WARNING': for input_port in node.input_ports: - if input_port.require_projection_in_composition and not input_port.path_afferents: + if input_port.require_projection_in_composition \ + and not input_port.path_afferents and not input_port.default_input: warnings.warn(f"{InputPort.__name__} ('{input_port.name}') of '{node.name}' " f"doesn't have any afferent {Projection.__name__}s.") for output_port in node.output_ports: @@ -7828,19 +7840,20 @@ def _get_control_signals_for_composition(self): control_signal_specs.extend(node._get_parameter_port_deferred_init_control_specs()) return control_signal_specs - # def _get_controller(comp, context=None): - # """Get controller for which the current Composition is an agent_rep. - # Recursively search enclosing Compositions for controller if self does not have one. - # Use context.composition if there is no controller. - # This is needed for agent_rep that is nested within the Composition to which the controller belongs. - # """ - # context = context or Context(source=ContextFlags.COMPOSITION, composition=None) - # if comp.controller: - # return comp.controller - # elif context.composition: - # return context.composition._get_controller(context) - # else: - # assert False, f"PROGRAM ERROR: Can't find controller for {comp.name}." + def _get_controller(self, comp=None, context=None): + """Get controller for which the current Composition is an agent_rep. + Recursively search enclosing Compositions for controller if self does not have one. + Use context.composition if there is no controller. + This is needed for agent_rep that is nested within the Composition to which the controller belongs. + """ + comp = comp or self + context = context or Context(source=ContextFlags.COMPOSITION, composition=None) + if comp.controller: + return comp.controller + elif context.composition: + return context.composition._get_controller(context=context) + else: + assert False, f"PROGRAM ERROR: Can't find controller for {comp.name}." def reshape_control_signal(self, arr): @@ -8015,28 +8028,37 @@ def _check_nodes_initialization_status(self, context=None): # FIX: 11/3/21 ??GET RID OF THIS AND CALL TO IT ONCE PROJECTIONS HAVE BEEN IMPLEMENTED FOR SHADOWED INPUTS # CHECK WHETHER state_input_ports ADD TO OR REPLACE shadowed_inputs - def _build_predicted_inputs_dict(self, predicted_input): + def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): """Format predict_inputs from controller as input to evaluate method used to execute simulations of Composition. Get values of state_input_ports that receive projections from items providing relevant input (and any processing of those values specified), and format as input_dict suitable for run() method (called in evaluate) Deal with inputs for nodes in nested Compositions """ + controller = controller or self.controller + # Use keys for inputs dict from OptimizationControlMechanism state_features if it is specified as a dict + # (unless it has SHADOW_INPUTS entry, in which case that is handled below) + input_dict_keys = (list(controller.state_features.keys()) + if (hasattr(controller, STATE_FEATURES) + and isinstance(controller.state_features, dict) + and SHADOW_INPUTS not in controller.state_features) + else None) inputs = {} - no_predicted_input = (predicted_input is None or not len(predicted_input)) + no_predicted_input = (predicted_inputs is None or not len(predicted_inputs)) if no_predicted_input: warnings.warn(f"{self.name}.evaluate() called without any inputs specified; default values will be used") nested_nodes = dict(self._get_nested_nodes()) # FIX: 11/3/21 NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED - shadow_inputs_start_index = self.controller.num_outcome_input_ports - for j in range(len(self.controller.input_ports) - shadow_inputs_start_index): - input_port = self.controller.input_ports[j + shadow_inputs_start_index] + shadow_inputs_start_index = controller.num_outcome_input_ports + for j in range(len(controller.input_ports) - shadow_inputs_start_index): + input_port = controller.input_ports[j + shadow_inputs_start_index] if no_predicted_input: - shadowed_input = input_port.defaults.value + predicted_input = input_port.defaults.value else: - shadowed_input = predicted_input[j] + predicted_input = predicted_inputs[j] + # Shadow input specified if hasattr(input_port, SHADOW_INPUTS) and input_port.shadow_inputs is not None: shadow_input_owner = input_port.shadow_inputs.owner if self._controller_initialization_status == ContextFlags.DEFERRED_INIT \ @@ -8046,8 +8068,9 @@ def _build_predicted_inputs_dict(self, predicted_input): if shadow_input_owner in nested_nodes: comp = nested_nodes[shadow_input_owner] if comp in inputs: - inputs[comp]=np.concatenate([[shadowed_input],inputs[comp][0]]) + inputs[comp]=np.concatenate([[predicted_input],inputs[comp][0]]) else: + # FIX 1/9/22: CAN THIS BE REPLACED BY CALL TO _get_source? def _get_enclosing_comp_for_node(input_port, comp): """Get the Composition that is a node of self in which node nested in it. - input_port is of node for which enclosing comp is being sought @@ -8067,14 +8090,26 @@ def _get_enclosing_comp_for_node(input_port, comp): comp_for_input = _get_enclosing_comp_for_node(input_port.shadow_inputs, shadow_input_comp) if comp_for_input in inputs: # If node for nested comp is already in inputs dict, append to its input - inputs[comp_for_input][0].append(shadowed_input) + inputs[comp_for_input][0].append(predicted_input) else: # Create entry in inputs dict for nested comp containing shadowed_input - inputs[comp_for_input]=[[shadowed_input]] + inputs[comp_for_input] = [[predicted_input]] else: if isinstance(shadow_input_owner, CompositionInterfaceMechanism): shadow_input_owner = shadow_input_owner.composition - inputs[shadow_input_owner] = shadowed_input + # Use key from OptimizationControlMechanism state_features dict if specified, else the source node + key = input_dict_keys[j] if input_dict_keys else shadow_input_owner + inputs[key] = predicted_input + + # Regular input specified (i.e., projection from an OutputPort) + else: + # assert len(input_port.path_afferents) == 1 + if input_port.path_afferents: + source = input_port.path_afferents[0].sender.owner + # Use key from OptimizationControlMechanism state_features dict if specified, else the source node + key = input_dict_keys[j] if input_dict_keys else source + inputs[key] = predicted_input + return inputs def _get_total_cost_of_control_allocation(self, control_allocation, context, runtime_params): @@ -8094,7 +8129,7 @@ def get_controller(comp): else: assert False, f"PROGRAM ERROR: Can't find controller for {self.name}." - controller = get_controller(self) + controller = self._get_controller(context=context) base_control_allocation = self.reshape_control_signal(controller.parameters.value._get(context)) candidate_control_allocation = self.reshape_control_signal(control_allocation) @@ -8168,6 +8203,8 @@ def evaluate( an array with the results of each run is also returned. """ + controller = self._get_controller(context=context) + # Build input dictionary for simulation input_spec = self.parameters.input_specification.get(context) if input_spec and block_simulate and not isgenerator(input_spec): @@ -8176,7 +8213,8 @@ def evaluate( elif isinstance(input_spec, dict): inputs = input_spec else: - inputs = self._build_predicted_inputs_dict(predicted_input) + inputs = self._build_predicted_inputs_dict(predicted_input, + controller=controller) if hasattr(self, '_input_spec') and block_simulate and isgenerator(input_spec): warnings.warn(f"The evaluate method of {self.name} is attempting to use block simulation, but the " @@ -8233,22 +8271,22 @@ def evaluate( # COMPUTE net_outcome and aggregate in net_outcomes # Update input ports in order to get correct value for "outcome" (from objective mech) - self.controller._update_input_ports(runtime_params, context) + controller._update_input_ports(runtime_params, context) # FIX: REFACTOR TO CREATE ARRAY OF INPUT_PORT VALUES FOR OUTCOME_INPUT_PORTS - outcome_is_array = self.controller.num_outcome_input_ports > 1 + outcome_is_array = controller.num_outcome_input_ports > 1 if not outcome_is_array: - outcome = self.controller.input_port.parameters.value._get(context) + outcome = controller.input_port.parameters.value._get(context) else: outcome = [] - for i in range(self.controller.num_outcome_input_ports): - outcome.append(self.controller.parameters.input_ports._get(context)[i].parameters.value._get(context)) + for i in range(controller.num_outcome_input_ports): + outcome.append(controller.parameters.input_ports._get(context)[i].parameters.value._get(context)) if outcome is None: net_outcome = 0.0 else: # Compute net outcome based on the cost of the simulated control allocation (usually, net = outcome - cost) - net_outcome = self.controller.compute_net_outcome(outcome, total_cost) + net_outcome = controller.compute_net_outcome(outcome, total_cost) if return_results: return net_outcome, result @@ -8330,7 +8368,7 @@ def _recursive_update(d, u): # 3) Resize inputs to be of the form [[[]]], # where each level corresponds to: > > - inputs, num_inputs_sets = self._parse_dict(inputs) + inputs, num_inputs_sets = self._parse_input_dict(inputs) return inputs, num_inputs_sets @@ -8394,7 +8432,7 @@ def _parse_list(self, inputs): raise CompositionError( f"Inputs to {self.name} must be specified in a dictionary with a key for each of its " f"{len(input_nodes)} INPUT nodes ({[n.name for n in input_nodes]}).") - input_dict, num_inputs_sets = self._parse_dict(_inputs) + input_dict, num_inputs_sets = self._parse_input_dict(_inputs) return input_dict, num_inputs_sets def _parse_string(self, inputs): @@ -8423,7 +8461,7 @@ def _parse_string(self, inputs): raise CompositionError( f"Inputs to {self.name} must be specified in a dictionary with a key for each of its " f"{len(input_nodes)} INPUT nodes ({[n.name for n in input_nodes]}).") - input_dict, num_inputs_sets = self._parse_dict(_inputs) + input_dict, num_inputs_sets = self._parse_input_dict(_inputs) return input_dict, num_inputs_sets def _parse_function(self, inputs): @@ -8457,7 +8495,8 @@ def _validate_single_input(self, node, input): """ # Validate that a single input is properly formatted for a node. _input = [] - node_variable = [input_port.defaults.value for input_port in node.input_ports if not input_port.internal_only] + node_variable = [input_port.defaults.value for input_port in node.input_ports + if not input_port.internal_only or input_port.default_input] match_type = self._input_matches_variable(input, node_variable) # match_type = self._input_matches_variable(input, node_variable) if match_type == 'homogeneous': @@ -8557,7 +8596,7 @@ def _flatten_nested_dicts(self, inputs): if node.componentType == 'Composition' and type(inp) == dict: # If there are multiple levels of nested dicts, we need to convert them starting from the deepest level, # so recurse down the chain here - inp, num_trials = node._parse_dict(inp) + inp, num_trials = node._parse_input_dict(inp) translated_stimulus_dict = {} # first time through the stimulus dictionary, assemble a dictionary in which the keys are input CIM @@ -8652,9 +8691,9 @@ def _parse_labels(self, inputs, mech=None, context=None): _inputs.append(stimulus) return _inputs - def _parse_dict(self, inputs, context=None): + def _parse_input_dict(self, inputs, context=None): """ - Validates and parses a dict provided as input to a Composition into a standardized form to be used throughout + Validate and parse a dict provided as input to a Composition into a standardized form to be used throughout its execution Returns @@ -8663,33 +8702,34 @@ def _parse_dict(self, inputs, context=None): Parsed and standardized input dict `int` : - Number of input sets in dict for each input node in the Composition + Number of input sets (i.e., trials' worths of inputs) in dict for each input node in the Composition """ # parse a user-provided input dict to format it properly for execution. # compute number of input sets and return that as well _inputs = self._parse_names_in_inputs(inputs) _inputs = self._parse_labels(_inputs) - _inputs = self._validate_input_dict_node_roles(_inputs) + _inputs = self._instantiate_input_dict(_inputs) _inputs = self._flatten_nested_dicts(_inputs) _inputs = self._validate_input_shapes(_inputs) - num_inputs_sets = len(next(iter(_inputs.values()))) + num_inputs_sets = len(next(iter(_inputs.values()),[])) return _inputs, num_inputs_sets - def _validate_input_dict_node_roles(self, inputs): - """ - Validates that all nodes included in input dict are input nodes. Additionally, if any input nodes are not - included, adds them to the input dict using their default values as entries + def _instantiate_input_dict(self, inputs): + """Implement dict with all INPUT Nodes of Composition as keys and their assigned inputs or defaults as values + Validate that all Nodes included in input dict are INPUT nodes of Composition. + If any input nodes of Composition are not included, add them to the input dict using their default values. Returns ------- `dict` : - The input dict, with added entries for any input nodes for which input was not provided - + Input dict, with added entries for any input Nodes for which input was not provided """ - # STEP 1A: Check that all of the nodes listed in the inputs dict are INPUT nodes in the composition + + # Check that all of the Nodes listed in the inputs dict are INPUT Nodes in the Composition input_nodes = self.get_nodes_by_role(NodeRole.INPUT) + for node in inputs.keys(): if node not in input_nodes: if not isinstance(node, (Mechanism, Composition)): @@ -8698,7 +8738,7 @@ def _validate_input_dict_node_roles(self, inputs): else: raise CompositionError(f"{node.name} in inputs dict for {self.name} is not one of its INPUT nodes.") - # STEP 1B: Check that all of the INPUT nodes are represented - if not, use default_external_input_values + # If any INPUT Nodes of the Composition are not specified, add and assign default_external_input_values for node in input_nodes: if node not in inputs: inputs[node] = node.default_external_input_values @@ -8717,7 +8757,7 @@ def _parse_run_inputs(self, inputs, context=None): """ # handle user-provided input based on input type. return processd inputs and num_inputs_sets if not inputs: - _inputs, num_inputs_sets = self._parse_dict({}) + _inputs, num_inputs_sets = self._parse_input_dict({}) elif isgeneratorfunction(inputs): _inputs, num_inputs_sets = self._parse_generator_function(inputs) elif isgenerator(inputs): @@ -8727,7 +8767,7 @@ def _parse_run_inputs(self, inputs, context=None): elif type(inputs) == list: _inputs, num_inputs_sets = self._parse_list(inputs) elif type(inputs) == dict: - _inputs, num_inputs_sets = self._parse_dict(inputs) + _inputs, num_inputs_sets = self._parse_input_dict(inputs) elif type(inputs) == str: _inputs, num_inputs_sets = self._parse_string(inputs) else: @@ -8754,7 +8794,7 @@ def _parse_trial_inputs(self, inputs, trial_num): # this method is intended to run BEFORE a call to Composition.execute if callable(inputs): try: - inputs, _ = self._parse_dict(inputs(trial_num)) + inputs, _ = self._parse_input_dict(inputs(trial_num)) i = 0 except TypeError as e: error_text = e.args[0] @@ -8763,7 +8803,7 @@ def _parse_trial_inputs(self, inputs, trial_num): else: raise CompositionError(f"Problem with function provided to 'inputs' arg of {self.name}.run") elif isgenerator(inputs): - inputs, _ = self._parse_dict(inputs.__next__()) + inputs, _ = self._parse_input_dict(inputs.__next__()) i = 0 else: num_inputs_sets = len(next(iter(inputs.values()))) @@ -8787,7 +8827,7 @@ def _validate_execution_inputs(self, inputs): _inputs = {} for node, inp in inputs.items(): if isinstance(node, Composition) and type(inp) == dict: - inp = node._parse_dict(inp) + inp = node._parse_input_dict(inp) inp = self._validate_single_input(node, inp) if inp is None: raise CompositionError(f"Input stimulus ({inp}) for {node.name} is incompatible " @@ -9767,7 +9807,7 @@ def execute( # if execute was called from command line and no inputs were specified, # assign default inputs to highest level composition (i.e. not on any nested Compositions) if not inputs and not nested and ContextFlags.COMMAND_LINE in context.source: - inputs = self._validate_input_dict_node_roles({}) + inputs = self._instantiate_input_dict({}) # Skip initialization if possible (for efficiency): # - and(context has not changed # - structure of the graph has not changed @@ -10419,6 +10459,7 @@ def __call__(self, *args, **kwargs): raise CompositionError(f"Composition ({self.name}) called with illegal argument(s): {bad_args_str}") + # Alias of get_input_format(easy mistake to make) def get_inputs_format(self, **kwargs): return self.get_input_format(**kwargs, alias="get_inputs_format") diff --git a/psyneulink/core/compositions/compositionfunctionapproximator.py b/psyneulink/core/compositions/compositionfunctionapproximator.py index 208dcbf4ac1..0623bb72ddb 100644 --- a/psyneulink/core/compositions/compositionfunctionapproximator.py +++ b/psyneulink/core/compositions/compositionfunctionapproximator.py @@ -27,10 +27,10 @@ A `CompositionFunctionApproximator` is an abstract subclass of `Composition` that, over calls to its `adapt ` method, parameterizes its `function ` to predict the `net_outcome ` of the Composition (or part of one) controlled by an -`OptimizationControlMechanism`, for a given set of `state_feature_values ` -and a `control_allocation ` provided by the OptimizationControlMechanism. -Its `evaluate ` method calls its `function -` to generate and return the predicted `net_outcome +`OptimizationControlMechanism`, for a given set of `state_feature_values +` and a `control_allocation ` +provided by the OptimizationControlMechanism. Its `evaluate ` method calls +its `function ` to generate and return the predicted `net_outcome ` for a given set of `state_feature_values `, `control_allocation `, `num_estimates `, and `num_trials_per_estimate @@ -54,8 +54,8 @@ """ from psyneulink.core.compositions.composition import Composition -from psyneulink.core.globals.keywords import COMPOSITION_FUNCTION_APPROXIMATOR from psyneulink.core.globals.context import Context +from psyneulink.core.globals.keywords import COMPOSITION_FUNCTION_APPROXIMATOR __all__ = ['CompositionFunctionApproximator'] diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index 90cbfa7fe2f..47d8ffaff8c 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -39,7 +39,7 @@ 'CONTROL_PROJECTIONS', 'CONTROL_SIGNAL', 'CONTROL_SIGNAL_SPECS', 'CONTROL_SIGNALS', 'CONTROLLED_PARAMS', 'CONTROLLER', 'CONTROLLER_OBJECTIVE', 'CORRELATION', 'COSINE', 'COST_FUNCTION', 'COUNT', 'CROSS_ENTROPY', 'CURRENT_EXECUTION_TIME', 'CUSTOM_FUNCTION', 'CYCLE', - 'DDM_MECHANISM', 'DECAY', 'DEFAULT', 'DEFAULT_CONTROL_MECHANISM', 'DEFAULT_MATRIX', + 'DDM_MECHANISM', 'DECAY', 'DEFAULT', 'DEFAULT_CONTROL_MECHANISM', 'DEFAULT_INPUT', 'DEFAULT_MATRIX', 'DEFAULT_PREFERENCE_SET_OWNER', 'DEFAULT_PROCESSING_MECHANISM', 'DEFAULT_VARIABLE', 'DEFERRED_ASSIGNMENT', 'DEFERRED_DEFAULT_NAME', 'DEFERRED_INITIALIZATION', 'DictionaryMemory_FUNCTION', 'DIFFERENCE', 'DIFFERENCE', 'DIFFUSION', 'DIRECT', 'DISABLE', 'DISABLE_PARAM', 'DIST_FUNCTION_TYPE', 'DIST_MEAN', @@ -818,6 +818,7 @@ def _is_metric(metric): WEIGHT = 'weight' EXPONENT = 'exponent' INTERNAL_ONLY = 'internal_only' +DEFAULT_INPUT = 'default_input' # ParameterPorts: PARAMETER_PORTS = 'parameter_ports' diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index a1588572a28..a75dad0b5fa 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -176,7 +176,7 @@ def test_deferred_init(self, control_spec): # add the controller to the Composition before adding the relevant Mechanisms comp.add_controller(controller=pnl.OptimizationControlMechanism( agent_rep=comp, - state_features=[Input.input_port, reward.input_port], + state_features=[reward.input_port, Input.input_port], state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), @@ -297,13 +297,11 @@ def test_partial_deferred_init(self): ]) ) - expected_text_1 = f"{ocomp.controller.name}, being used as controller for " \ - f"model-based optimization of {ocomp.name}, has 'state_features' specified " - expected_text_2 = f"that are missing from the Composition or any nested within it" + expected_text = 'The number of \'state_features\' specified for Controller (2) is more ' \ + 'than the number of INPUT Nodes (1) of the Composition assigned as its agent_rep (\'ocomp\').' with pytest.raises(pnl.OptimizationControlMechanismError) as error_text: ocomp.run({initial_node_a: [1]}) - error_text = error_text.value.error_value - assert expected_text_1 in error_text and expected_text_2 in error_text + assert expected_text in error_text.value.error_value ocomp.add_linear_processing_pathway([deferred_node, initial_node_b]) result = ocomp.run({ @@ -315,6 +313,55 @@ def test_partial_deferred_init(self): # Control Signal "deferred_node": Maximizes over the search space consisting of ints 1-5 assert result == [[10]] + def test_deferred_objective_mech(self): + initial_node = pnl.TransferMechanism(name='initial_node') + deferred_node = pnl.ProcessingMechanism(name='deferred') + ocomp = pnl.Composition(name='ocomp', + pathways=[initial_node], + controller_mode=pnl.BEFORE) + + initial_node_control_signal = pnl.ControlSignal(projections=[(pnl.SLOPE, initial_node)], + variable=1.0, + intensity_cost_function=pnl.Linear(slope=0.0), + allocation_samples=pnl.SampleSpec(start=1.0, + stop=5.0, + num=5)) + ocomp.add_controller( + pnl.OptimizationControlMechanism( + agent_rep=ocomp, + state_features=[initial_node.input_port], + name="Controller", + objective_mechanism=pnl.ObjectiveMechanism( + monitor=deferred_node.output_port, + function=pnl.SimpleIntegrator, + name="oController Objective Mechanism" + ), + function=pnl.GridSearch(direction=pnl.MAXIMIZE), + control_signals=[ + initial_node_control_signal + ]) + ) + + text = '"Controller has \'outcome_ouput_ports\' that receive Projections from the following Components ' \ + 'that do not belong to its agent_rep (ocomp): [\'deferred\']."' + with pytest.raises(pnl.OptimizationControlMechanismError) as error: + ocomp.run({initial_node: [1]}) + assert text == str(error.value) + + # The objective Mechanism is disabled because one of its aux components is a projection to + # deferred_node, which is not currently a member node of the composition. Therefore, the Controller + # has no basis to determine which set of values it should use for its efferent ControlProjections and + # simply goes with the first in the search space, which is 1. + + # add deferred_node to the Composition + ocomp.add_linear_processing_pathway([initial_node, deferred_node]) + + # The objective mechanism's aux components are now all legal, so it will be activated on the following run + result = ocomp.run({initial_node: [[1]]}) + assert result == [[5]] + # result = 5, the input (1) multiplied by the value of the ControlSignal projecting to Node "ia" + # Control Signal "ia": Maximizes over the search space consisting of ints 1-5 + def test_warning_for_add_controller_twice(self): mech = pnl.ProcessingMechanism() ctlr_1 = pnl.ControlMechanism() @@ -362,185 +409,6 @@ def test_controller_has_no_input(self): comp.add_controller(ctlr) assert expected_warning in repr(warning[0].message.args[0]) - # FIX: DEPRACATE THIS TEST - IT ALLOWS A COMPOSITION TO EXECUTE WITH A BAD MONITOR FOR CONTROL SPECIFICATION - # SUPERCEDED BY test_args_specific_to_ocm outcome_input_ports WHICH TESTS FOR THIS - # def test_deferred_objective_mech(self): - # initial_node = pnl.TransferMechanism(name='initial_node') - # deferred_node = pnl.ProcessingMechanism(name='deferred') - # ocomp = pnl.Composition(name='ocomp', - # pathways=[initial_node], - # controller_mode=pnl.BEFORE) - # - # initial_node_control_signal = pnl.ControlSignal(projections=[(pnl.SLOPE, initial_node)], - # variable=1.0, - # intensity_cost_function=pnl.Linear(slope=0.0), - # allocation_samples=pnl.SampleSpec(start=1.0, - # stop=5.0, - # num=5)) - # - # ocomp.add_controller( - # pnl.OptimizationControlMechanism( - # agent_rep=ocomp, - # state_features=[initial_node.input_port], - # name="Controller", - # objective_mechanism=pnl.ObjectiveMechanism( - # monitor=deferred_node.output_port, - # function=pnl.SimpleIntegrator, - # name="oController Objective Mechanism" - # ), - # function=pnl.GridSearch(direction=pnl.MAXIMIZE), - # control_signals=[ - # initial_node_control_signal - # ]) - # ) - # - # text = 'The controller of ocomp has a specification that includes the '\ - # 'Mechanism oController Objective Mechanism, but oController '\ - # 'Objective Mechanism is not in ocomp or any of its nested Compositions. '\ - # 'This Mechanism will be deactivated until oController Objective Mechanism is '\ - # 'added to ocomp or one of its nested Compositions in a compatible way.' - # with pytest.warns(UserWarning, match=text): - # result = ocomp.run({initial_node: [1]}) - # - # assert result == [[1]] - # # result = 1, the input (1) multiplied by the first value in the SearchSpace of the ControlSignal projecting to - # # initial_node (1) - # - # # The objective Mechanism is disabled because one of its aux components is a projection to - # # deferred_node, which is not currently a member node of the composition. Therefore, the Controller - # # has no basis to determine which set of values it should use for its efferent ControlProjections and - # # simply goes with the first in the search space, which is 1. - # - # # add deferred_node to the Composition - # ocomp.add_linear_processing_pathway([initial_node, deferred_node]) - # - # # The objective mechanism's aux components are now all legal, so it will be activated on the following run - # result = ocomp.run({initial_node: [[1]]}) - # assert result == [[5]] - # # result = 5, the input (1) multiplied by the value of the ControlSignal projecting to Node "ia" - # # Control Signal "ia": Maximizes over the search space consisting of ints 1-5 - - # id, agent_rep, state_feat, mon_for_ctl, allow_probes, obj_mech err_type, error_msg - params = [ - ("allowable1", - "icomp", "I", "I", True, None, None, None - ), - ("allowable2", - "mcomp", "Ii A", "I B", True, None, None, None - ), - ("state_features_test_internal", - "icomp", "B", "I", True, None, pnl.CompositionError, - "Attempt to shadow the input to a node (B) in a nested Composition of OUTER COMP " - "that is not an INPUT Node of that Composition is not currently supported." - ), - ("state_features_test_not_in_agent_rep", - "icomp", "A", "I", True, None, pnl.OptimizationControlMechanismError, - "OCM, being used as controller for model-based optimization of INNER COMP, has 'state_features' " - "specified (['Shadowed input of A']) that are missing from the Composition or any nested within it." - ), - ("monitor_for_control_test_not_in_agent_rep", - "icomp", "I", "B", True, None, pnl.OptimizationControlMechanismError, - "OCM has 'outcome_ouput_ports' that receive Projections from the following Components " - "that do not belong to its agent_rep (INNER COMP): ['B']." - ), - ("monitor_for_control_with_obj_mech_test", - "icomp", "I", None, True, True, pnl.OptimizationControlMechanismError, - "OCM has 'outcome_ouput_ports' that receive Projections from the following Components " - "that do not belong to its agent_rep (INNER COMP): ['B']." - ), - ("probe_error_test", - "mcomp", "I", "B", False, None, pnl.CompositionError, - "B found in nested Composition of OUTER COMP (MIDDLE COMP) but without " - "required NodeRole.OUTPUT. Try setting 'allow_probes' argument of OCM to 'True'." - ), - ("probe_error_obj_mech_test", - "mcomp", "I", None, False, True, pnl.CompositionError, - "B found in nested Composition of OUTER COMP (MIDDLE COMP) but without required NodeRole.OUTPUT. " - "Try setting 'allow_probes' argument of ObjectiveMechanism for OCM to 'True'." - ) - ] - @pytest.mark.parametrize('id, agent_rep, state_features, monitor_for_control, allow_probes, objective_mechanism, error_type, err_msg', - params, ids=[x[0] for x in params]) - def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_control, - allow_probes, objective_mechanism, error_type,err_msg): - """Test args specific to OptimizationControlMechanism - - state_feature must be in agent_rep - - monitor_for_control must be in agent_rep, whether specified directly or for ObjectiveMechanism - - allow_probes allows INTERNAL Nodes of nested comp to be monitored, otherwise generates and error - - probes are not included in Composition.results - """ - - # FIX: ADD VERSION WITH agent_rep = CompositionFuntionApproximator - # ADD TESTS FOR SEPARATE AND CONCATENATE - - from psyneulink.core.globals.utilities import convert_to_list - - I = pnl.ProcessingMechanism(name='I') - icomp = pnl.Composition(nodes=I, name='INNER COMP') - - A = pnl.ProcessingMechanism(name='A') - B = pnl.ProcessingMechanism(name='B') - C = pnl.ProcessingMechanism(name='C') - mcomp = pnl.Composition(pathways=[[A,B,C],icomp], - name='MIDDLE COMP') - ocomp = pnl.Composition(nodes=[mcomp], name='OUTER COMP', allow_probes=allow_probes) - - agent_rep = {"mcomp":mcomp, - "icomp":icomp - }[agent_rep] - - state_features = {"I":I, - "Ii A":[I.input_port, A], - "A":A, - "B":B, - }[state_features] - - if monitor_for_control: - monitor_for_control = {"I":I, - "I B":[I, B], - "B":B, - }[monitor_for_control] - - if objective_mechanism: - objective_mechanism = pnl.ObjectiveMechanism(monitor=B) - - if not err_msg: - ocm = pnl.OptimizationControlMechanism(name='OCM', - agent_rep=agent_rep, - state_features=state_features, - monitor_for_control=monitor_for_control, - objective_mechanism=objective_mechanism, - allow_probes=allow_probes, - function=pnl.GridSearch(), - control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I), - allocation_samples=[10, 20, 30]) - ) - ocomp.add_controller(ocm) - ocomp._analyze_graph() - if allow_probes and B in convert_to_list(monitor_for_control): - # If this fails, could be due to ordering of ports in ocomp.output_CIM (current assumes probe is on 0) - assert ocomp.output_CIM._sender_is_probe(ocomp.output_CIM.output_ports[0]) - # Affirm that PROBE (included in ocomp's output_ports via its output_CIM - # but is *not* included in Composition.output_values (which is used for Composition.results) - assert len(ocomp.output_values) == len(ocomp.output_ports) - 1 - - else: - with pytest.raises(error_type) as err: - ocm = pnl.OptimizationControlMechanism(name='OCM', - agent_rep=agent_rep, - state_features=state_features, - monitor_for_control=monitor_for_control, - objective_mechanism=objective_mechanism, - allow_probes=allow_probes, - function=pnl.GridSearch(), - control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE, - I), - allocation_samples=[10, 20, 30]) - ) - ocomp.add_controller(ocm) - ocomp._analyze_graph() - assert err.value.error_value == err_msg - def test_agent_rep_assignement_as_controller_and_replacement(self): mech = pnl.ProcessingMechanism() comp = pnl.Composition(name='comp', @@ -744,52 +612,204 @@ def test_transfer_mechanism_and_ocm_variations( class TestControlMechanisms: - messages = ["The 'state_features' argument has been specified for 'OptimizationControlMechanism-0', " - "that is being configured to use a Composition ('OUTER COMP') as its 'agent_rep'). " - "This overrides automatic assignment of its 'state_features' as inputs to 'OUTER COMP' " - "when it is executed. If they are not properly configured, it will cause an error. " - "Remove this specification from the constructor for 'OptimizationControlMechanism-0' " - "to automatically configure its 'state_features' to be the external inputs to 'OUTER COMP'.", + # id, agent_rep, state_feat, mon_for_ctl, allow_probes, obj_mech err_type, error_msg + params = [ + ("allowable1", + "icomp", "I", "I", True, None, None, None + ), + ("allowable2", + "mcomp", "Ii A", "I B", True, None, None, None + ), + ("state_features_test_internal", + "icomp", "B", "I", True, None, pnl.CompositionError, + "Attempt to shadow the input to a node (B) in a nested Composition of OUTER COMP " + "that is not an INPUT Node of that Composition is not currently supported." + ), + ("state_features_test_not_in_agent_rep", + "icomp", "A", "I", True, None, pnl.OptimizationControlMechanismError, + '\'OCM\' has \'state_features\' specified ([\'Shadowed input of A\']) that are missing from both its ' + '`agent_rep` (\'INNER COMP\') as well as \'OUTER COMP\' and any Compositions nested within it.' + ), + ("monitor_for_control_test_not_in_agent_rep", + "icomp", "I", "B", True, None, pnl.OptimizationControlMechanismError, + "OCM has 'outcome_ouput_ports' that receive Projections from the following Components " + "that do not belong to its agent_rep (INNER COMP): ['B']." + ), + ("monitor_for_control_with_obj_mech_test", + "icomp", "I", None, True, True, pnl.OptimizationControlMechanismError, + "OCM has 'outcome_ouput_ports' that receive Projections from the following Components " + "that do not belong to its agent_rep (INNER COMP): ['B']." + ), + ("probe_error_test", + "mcomp", "I", "B", False, None, pnl.CompositionError, + "B found in nested Composition of OUTER COMP (MIDDLE COMP) but without " + "required NodeRole.OUTPUT. Try setting 'allow_probes' argument of OCM to 'True'." + ), + ("probe_error_obj_mech_test", + "mcomp", "I", None, False, True, pnl.CompositionError, + "B found in nested Composition of OUTER COMP (MIDDLE COMP) but without required NodeRole.OUTPUT. " + "Try setting 'allow_probes' argument of ObjectiveMechanism for OCM to 'True'." + ) + ] + @pytest.mark.parametrize('id, agent_rep, state_features, monitor_for_control, allow_probes, ' + 'objective_mechanism, error_type, err_msg', + params, ids=[x[0] for x in params]) + def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_control, + allow_probes, objective_mechanism, error_type,err_msg): + """Test args specific to OptimizationControlMechanism + NOTE: state_features and associated warning and errors tested more fully in + test_ocm_state_feature_specs_and_warnings_and_errors() below + - state_feature must be in agent_rep + - monitor_for_control must be in agent_rep, whether specified directly or for ObjectiveMechanism + - allow_probes allows INTERNAL Nodes of nested comp to be monitored, otherwise generates and error + - probes are not included in Composition.results + """ - '\'Attempt to shadow the input to a node (IB) in a nested Composition of OUTER COMP ' - 'that is not an INPUT Node of that Composition is not currently supported.\'', + # FIX: ADD VERSION WITH agent_rep = CompositionFuntionApproximator + # ADD TESTS FOR SEPARATE AND CONCATENATE - '"OptimizationControlMechanism-0, being used as controller for model-based optimization of OUTER COMP, ' - 'has \'state_features\' specified ([\'Shadowed input of EXT\']) that are missing from the ' - 'Composition or any nested within it."', + from psyneulink.core.globals.utilities import convert_to_list - '"OptimizationControlMechanism-0, being used as controller for model-based optimization of OUTER COMP, ' - 'has \'state_features\' specified ([\'EXT[OutputPort-0]\']) that are missing from the ' - 'Composition or any nested within it."' - ] + I = pnl.ProcessingMechanism(name='I') + icomp = pnl.Composition(nodes=I, name='INNER COMP') - state_feature_specs = ['legal_feature', 'misplaced_shadow', 'ext_shadow', 'ext_output_port'] + A = pnl.ProcessingMechanism(name='A') + B = pnl.ProcessingMechanism(name='B') + C = pnl.ProcessingMechanism(name='C') + mcomp = pnl.Composition(pathways=[[A,B,C],icomp], + name='MIDDLE COMP') + ocomp = pnl.Composition(nodes=[mcomp], name='OUTER COMP', allow_probes=allow_probes) + + agent_rep = {"mcomp":mcomp, + "icomp":icomp + }[agent_rep] + + state_features = {"I":I, + "Ii A":[I.input_port, A], + "A":A, + "B":B, + }[state_features] + + if monitor_for_control: + monitor_for_control = {"I":I, + "I B":[I, B], + "B":B, + }[monitor_for_control] + + if objective_mechanism: + objective_mechanism = pnl.ObjectiveMechanism(monitor=B) + + if not err_msg: + ocm = pnl.OptimizationControlMechanism(name='OCM', + agent_rep=agent_rep, + state_features=state_features, + monitor_for_control=monitor_for_control, + objective_mechanism=objective_mechanism, + allow_probes=allow_probes, + function=pnl.GridSearch(), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I), + allocation_samples=[10, 20, 30]) + ) + ocomp.add_controller(ocm) + ocomp._analyze_graph() + if allow_probes and B in convert_to_list(monitor_for_control): + # If this fails, could be due to ordering of ports in ocomp.output_CIM (current assumes probe is on 0) + assert ocomp.output_CIM._sender_is_probe(ocomp.output_CIM.output_ports[0]) + # Affirm that PROBE (included in ocomp's output_ports via its output_CIM + # but is *not* included in Composition.output_values (which is used for Composition.results) + assert len(ocomp.output_values) == len(ocomp.output_ports) - 1 + + else: + with pytest.raises(error_type) as err: + ocm = pnl.OptimizationControlMechanism(name='OCM', + agent_rep=agent_rep, + state_features=state_features, + monitor_for_control=monitor_for_control, + objective_mechanism=objective_mechanism, + allow_probes=allow_probes, + function=pnl.GridSearch(), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE, + I), + allocation_samples=[10, 20, 30]) + ) + ocomp.add_controller(ocm) + ocomp._analyze_graph() + assert err.value.error_value == err_msg + + messages = [ + "The 'state_features' specified for 'OptimizationControlMechanism-0' are legal, " + "but there are fewer than the number of input_nodes for its agent_rep ('OUTER COMP'); " + "the remaining inputs will be assigned default values. " + "Use the agent_rep's get_inputs_format() method to see the format for its inputs.", + + '\'Attempt to shadow the input to a node (IB) in a nested Composition of OUTER COMP ' + 'that is not an INPUT Node of that Composition is not currently supported.\'', + + '"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'Shadowed input of EXT\']) that ' + 'are missing from \'OUTER COMP\' and any Compositions nested within it."', + + '"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'EXT[OutputPort-0]\']) ' + 'that are missing from \'OUTER COMP\' and any Compositions nested within it."', + + "The 'state_features' argument has been specified for 'OptimizationControlMechanism-0' that is using a " + "Composition ('OUTER COMP') as its agent_rep, but they are not compatible with the inputs required by its " + "'agent_rep': 'Input stimulus (0.0) for OB is incompatible with its external_input_values " + "([array([0., 0., 0.])]).' Use the get_inputs_format() method of 'OUTER COMP' to see the required format, " + "or remove the specification of 'state_features' from the constructor for OptimizationControlMechanism-0 to " + "have them automatically assigned.", + + '"The number of \'state_features\' specified for OptimizationControlMechanism-0 (4) is more than the number ' + 'of INPUT Nodes (3) of the Composition assigned as its agent_rep (\'OUTER COMP\')."' + ] + + state_feature_specs = ['partial_legal_ports_spec', + 'full_legal_ports_spec', + 'input_dict_spec', + 'shadow_inputs_dict_spec', + 'misplaced_shadow', + 'ext_shadow', + 'ext_output_port', + 'bad_input_format_spec_wrong_shape', + 'bad_input_format_spec_too_many' + ] state_feature_args = [ (state_feature_specs[0], messages[0], UserWarning), - (state_feature_specs[1], messages[1], pnl.CompositionError), - (state_feature_specs[2], messages[2], pnl.OptimizationControlMechanismError), - (state_feature_specs[3], messages[3], pnl.OptimizationControlMechanismError) + (state_feature_specs[1], None, None), + (state_feature_specs[2], None, None), + (state_feature_specs[3], None, None), + (state_feature_specs[4], messages[1], pnl.CompositionError), + (state_feature_specs[5], messages[2], pnl.OptimizationControlMechanismError), + (state_feature_specs[6], messages[3], pnl.OptimizationControlMechanismError), + (state_feature_specs[7], messages[4], pnl.OptimizationControlMechanismError), + (state_feature_specs[8], messages[5], pnl.OptimizationControlMechanismError) ] @pytest.mark.control - @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x for x in state_feature_specs]) - def test_ocm_state_input_ports_warnings_and_errors(self, state_feature_args): + @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x[0] for x in state_feature_args]) + def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_args): ia = pnl.ProcessingMechanism(name='IA') ib = pnl.ProcessingMechanism(name='IB') ic = pnl.ProcessingMechanism(name='IC') oa = pnl.ProcessingMechanism(name='OA') - ob = pnl.ProcessingMechanism(name='OB') + ob = pnl.ProcessingMechanism(name='OB', size=3) oc = pnl.ProcessingMechanism(name='OC') ext = pnl.ProcessingMechanism(name='EXT') icomp = pnl.Composition(pathways=[ia,ib,ic], name='INNER COMP') ocomp = pnl.Composition(pathways=[icomp], name='OUTER COMP') ocomp.add_linear_processing_pathway([oa,oc]) ocomp.add_linear_processing_pathway([ob,oc]) - state_features_dict = {'legal_feature':ia.input_port, - 'misplaced_shadow':ib.input_port, - 'ext_shadow':ext.input_port, - 'ext_output_port':ext.output_port} + state_features_dict = { + 'partial_legal_ports_spec': [oa.output_port], + 'full_legal_ports_spec': [ia.input_port, oa.output_port, [3,1,2]], + 'input_dict_spec': {icomp:ia.input_port, oa:oc.input_port, ob:ob.output_port}, + 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, + 'misplaced_shadow':ib.input_port, + 'ext_shadow':ext.input_port, + 'ext_output_port':ext.output_port, + 'bad_input_format_spec_wrong_shape': [ia.input_port, oa.output_port, oc.output_port], + 'bad_input_format_spec_too_many': [ia.input_port, oa.output_port, ob.output_port, oc.output_port] + } state_features = state_features_dict[state_feature_args[0]] message = state_feature_args[1] ocm = pnl.OptimizationControlMechanism(state_features=state_features, @@ -799,11 +819,30 @@ def test_ocm_state_input_ports_warnings_and_errors(self, state_feature_args): allocation_samples=[10, 20, 30]), pnl.ControlSignal(modulates=(pnl.INTERCEPT,oc), allocation_samples=[10, 20, 30])]) - if state_feature_args[2] is UserWarning: + + if not state_feature_args[1]: + + ocomp.add_controller(ocm) + ocomp.run() + + if state_feature_args[0] == 'partial_legal_ports_spec': + assert len(ocm.state_input_ports) == 1 + assert ocm.state_input_ports.names == ['OA[OutputPort-0]'] + + elif state_feature_args[0] == 'full_legal_ports_spec': + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['Shadowed input of IA', 'OA[OutputPort-0]', 'OB'] + + elif state_feature_args[0] == 'input_dict_spec': + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['INNER COMP', 'OA', 'OB'] + + elif state_feature_args[2] is UserWarning: with pytest.warns(UserWarning) as warning: ocomp.add_controller(ocm) ocomp.run() - assert warning[9].message.args[0] == message + assert warning[10].message.args[0] == message + else: with pytest.raises(state_feature_args[2]) as error: ocomp.add_controller(ocm) @@ -823,9 +862,10 @@ def test_ocm_state_and_state_dict(self): ocomp.add_linear_processing_pathway([oa,oc]) ocomp.add_linear_processing_pathway([ob,oc]) ocm = pnl.OptimizationControlMechanism( - state_features=[ia.input_port, # Note: these state_features will not execute properly - ib.output_port, # they are only for testing - oc], + state_features=[ia.input_port, + oa.output_port, + # ob], + [3,1,2]], objective_mechanism=[ic,ib], function=pnl.GridSearch(), allow_probes=True, @@ -836,15 +876,15 @@ def test_ocm_state_and_state_dict(self): ] ) ocomp.add_controller(ocm) - assert np.allclose(ocm.state, [[0.], [0.], [0.], [1.], [1.]]) + assert all(np.allclose(x,y) for x,y in zip(ocm.state, [[0.0], [0.0], [3.0, 1.0, 2.0], [1.0], [1.0]])) assert len(ocm.state_dict) == 6 keys = list(ocm.state_dict.keys()) values = list(ocm.state_dict.values()) for key, value in ocm.state_dict.items(): ocm.state[key[3]] == value assert keys[0] == (ia.input_port, ia, icomp ,0) - assert keys[1] == (ib.output_port, ib, icomp, 1) - assert keys[2] == (oc.input_port, oc, ocomp, 2) + assert keys[1] == (oa.output_port, oa, ocomp, 1) + assert keys[2] == ('default_variable', None, None, 2) assert keys[3] == (ia.parameter_ports[pnl.SLOPE], ia, icomp, 3) assert keys[4] == (oc.parameter_ports[pnl.INTERCEPT], oc, ocomp, 4) assert keys[5] == (oc.parameter_ports[pnl.SLOPE], oc, ocomp, 4) @@ -1750,7 +1790,7 @@ def test_evc(self): comp.add_controller(controller=pnl.OptimizationControlMechanism( agent_rep=comp, - state_features=[Input.input_port, reward.input_port], + state_features=[reward.input_port, Input.input_port], state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), @@ -2034,7 +2074,7 @@ def test_laming_validation_specify_control_signals(self): comp.add_controller( controller=pnl.OptimizationControlMechanism( agent_rep=comp, - state_features=[Input.input_port, reward.input_port], + state_features=[reward.input_port, Input.input_port], state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), @@ -2172,7 +2212,7 @@ def test_stateful_mechanism_in_simulation(self): comp.add_controller( controller=pnl.OptimizationControlMechanism( agent_rep=comp, - state_features=[Input.input_port, reward.input_port], + state_features=[reward.input_port, Input.input_port], state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), @@ -2759,7 +2799,7 @@ def test_input_CIM_assignment(self, comp_mode): comp.add_controller( pnl.OptimizationControlMechanism( agent_rep=comp, - state_features=[input_b.input_port, input_a.input_port], + state_features=[input_a.input_port, input_b.input_port], name="Controller", objective_mechanism=pnl.ObjectiveMechanism( monitor=output.output_port, @@ -2795,55 +2835,51 @@ def test_input_CIM_assignment(self, comp_mode): @pytest.mark.control @pytest.mark.composition - @pytest.mark.parametrize('nested_agent_rep', [ - 'unnested', - # 'nested' # FIX: RESTORE once nested Composition is supported for agent_rep - ]) - @pytest.mark.parametrize('bad_state_featues', [ - 'good_state_feat', - 'bad_state_feat' - ]) - def test_nested_composition_as_agent_rep(self, nested_agent_rep, bad_state_featues): - from psyneulink.core.compositions.composition import RunError + @pytest.mark.parametrize('nested_agent_rep', + [(False, 'OUTER COMP'),(True, 'MIDDLE COMP')], + ids=['unnested','nested']) + @pytest.mark.parametrize('state_features_arg', + ['good','bad'], + ids=['good_state_feat', 'bad_state_feat']) + def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_arg): + I = pnl.ProcessingMechanism(name='I') icomp = pnl.Composition(nodes=I, name='INNER COMP') - A = pnl.ProcessingMechanism(name='A') B = pnl.ProcessingMechanism(name='B') - C = pnl.ProcessingMechanism(name='C') - mcomp = pnl.Composition(pathways=[[A,B,C], icomp], - name='MIDDLE COMP') - agent_rep = mcomp if nested_agent_rep is 'nested' else None - state_features = I.input_port if bad_state_featues is 'bad_state_feat' else None + C = pnl.ProcessingMechanism(name='C', size=3) + D = pnl.ProcessingMechanism(name='D') + mcomp = pnl.Composition(pathways=[[A,B,C], icomp], name='MIDDLE COMP') ocomp = pnl.Composition(nodes=[mcomp], name='OUTER COMP') - ocm = pnl.OptimizationControlMechanism(name='OCM', - agent_rep=agent_rep, # Nested Composition as agent_rep - state_features=state_features, - objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), - allow_probes=True, - function=pnl.GridSearch(), - control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I), - allocation_samples=[10, 20, 30])) - ocomp.add_controller(ocm) - error_text = 'Input stimulus ([array([0.])]) for MIDDLE COMP is incompatible with its ' \ - 'external_input_values ([array([0.]), array([0.])]).' - if nested_agent_rep == 'unnested': - if bad_state_featues == 'bad_state_feat': - with pytest.raises(RunError) as error: - ocomp.run() - assert error_text in str(error.value) - else: - ocomp.run() - # FIX: CRASHES IN composition._get_total_cost_of_control_allocation() - # RESTORE 'nested' for nested_agent_rep arg (in params)once nested Composition is supported for agent_rep + # Test args: + if nested_agent_rep is True: + agent_rep = mcomp + error_text = f"'OCM' has 'state_features' specified (['D[OutputPort-0]']) that are missing from both " \ + f"its `agent_rep` ('{nested_agent_rep[1]}') as well as 'OUTER COMP' and any " \ + f"Compositions nested within it." else: - if bad_state_featues == 'bad_state_feat': - with pytest.raises(RunError) as error: - ocomp.run() - assert error_text in str(error.value) - ocomp.run() + agent_rep = None + error_text = '"\'OCM\' has \'state_features\' specified ([\'D[OutputPort-0]\']) that are ' \ + 'missing from \'OUTER COMP\' and any Compositions nested within it."' + state_features = D.output_port if state_features_arg is 'bad' else None + ocm = pnl.OptimizationControlMechanism(name='OCM', + agent_rep=agent_rep, + state_features=state_features, + objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), + allow_probes=True, + function=pnl.GridSearch(), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I), + allocation_samples=[10, 20, 30])) + if state_features_arg == 'bad': + with pytest.raises(pnl.OptimizationControlMechanismError) as error: + ocomp.add_controller(ocm) + ocomp.run() + assert error_text in str(error.value) + else: + ocomp.add_controller(ocm) + ocomp.run() class TestSampleIterator: diff --git a/tests/json/model_with_control.py b/tests/json/model_with_control.py index 17240875340..10b401d1f62 100644 --- a/tests/json/model_with_control.py +++ b/tests/json/model_with_control.py @@ -70,3 +70,5 @@ ], ) ) +comp._analyze_graph() +assert True diff --git a/tests/ports/test_input_ports.py b/tests/ports/test_input_ports.py index 242bc62f62d..5f7bc6dcb05 100644 --- a/tests/ports/test_input_ports.py +++ b/tests/ports/test_input_ports.py @@ -1,7 +1,7 @@ import numpy as np -import psyneulink as pnl import pytest +import psyneulink as pnl import psyneulink.core.components.functions.nonstateful.combinationfunctions import psyneulink.core.components.functions.nonstateful.transferfunctions @@ -96,3 +96,32 @@ def test_internal_only(self): m = pnl.TransferMechanism(input_ports=['EXTERNAL', pnl.InputPort(name='INTERNAL_ONLY', internal_only=True)]) assert m.input_values == [[ 0.],[ 0.]] assert m.external_input_values == [[0.]] + + @pytest.mark.parametrize('default_input', [None, pnl.DEFAULT_VARIABLE]) + def test_default_input(self, default_input): + variable = [22] + m = pnl.TransferMechanism(input_ports=[pnl.InputPort(name='INTERNAL_NODE', + default_input=default_input, + variable=variable)]) + m.execute() + assert m.input_port.value == variable + if default_input: + assert m.input_port.internal_only is True + else: + assert m.input_port.internal_only is False + comp = pnl.Composition(nodes=(m, pnl.NodeRole.INTERNAL)) + assert pnl.NodeRole.INTERNAL in comp.get_roles_by_node(m) + assert pnl.NodeRole.INPUT not in comp.get_roles_by_node(m) + assert not m.path_afferents + if default_input is None: + with pytest.warns(UserWarning) as warning: # Warn, since default_input is NOT set + comp.run() + assert repr(warning[1].message.args[0]) == '"InputPort (\'INTERNAL_NODE\') of \'TransferMechanism-0\' ' \ + 'doesn\'t have any afferent Projections."' + assert m.input_port.value == variable # For Mechanisms other than controller, default_variable seems + assert m.value == variable # to still be used even though default_input is NOT set + else: + assert not m.path_afferents # No path_afferents since internal_only is set by default_input + comp.run() # No warning since default_input is set + assert m.input_port.value == variable + assert m.value == variable From c91a3598cb601af57fed05254e3c9d1a006b328f Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sat, 15 Jan 2022 22:27:08 -0500 Subject: [PATCH 103/285] Feat/ocm/state features (#2292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - _update_state_input_ports_for_controller: - skip if ContextFlags.METHOD -> ContextFlags.PROCESSING * • optimizationcontrolmechanism.py: - add _validate_state_features - _update_state_input_ports_for_controller: validation of state_features moved to _validate_state_features * - * • optimizationcontrolmechanism.py - Parameters: add outcome_input_ports and state_input_ports * • controlmechanism.py & optimizationcontrolmechanism.py - move outcome_input_ports Parameter from ocm to controlmechanism - state_features: set to make Parameter on ocm, but waiting to deal with LLVM * • controlmechanism.py & optimizationcontrolmechanism.py - move outcome_input_ports Parameter from ocm to controlmechanism - state_features: set to make Parameter on ocm, but waiting to deal with LLVM * Passing all tests but ones in test_show_graph and predator_prey * • test_show_graph.py: - correct output for fix of typo • test_greedy_agent.py: - test_predator_prey(): reorder args in state_features to fix failure * - * - Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- .../modulatory/control/controlmechanism.py | 10 +- .../control/optimizationcontrolmechanism.py | 348 +++++++++--------- psyneulink/core/compositions/composition.py | 7 +- tests/composition/test_control.py | 42 ++- tests/composition/test_show_graph.py | 44 +-- tests/models/test_greedy_agent.py | 3 +- 6 files changed, 245 insertions(+), 209 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index 9bbe85601ef..4ab4c7b82c8 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1146,6 +1146,12 @@ class Parameters(ModulatoryMechanism_Base.Parameters): :type: :read only: True + outcome_input_ports + see `outcome_input_ports ` + + :default value: None + :type: ``list`` + output_ports see `output_ports ` @@ -1183,6 +1189,7 @@ class Parameters(ModulatoryMechanism_Base.Parameters): objective_mechanism = Parameter(None, stateful=False, loggable=False, structural=True) outcome_input_ports_option = Parameter(SEPARATE, stateful=False, loggable=False, structural=True) + outcome_input_ports = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) input_ports = Parameter( [OUTCOME], @@ -1550,7 +1557,8 @@ def _instantiate_input_ports(self, input_ports=None, context=None): other_input_ports = input_ports or [] # FIX 11/3/21: THIS SHOULD BE MADE A PARAMETER - self.outcome_input_ports = ContentAddressableList(component_type=OutputPort) + self.parameters.outcome_input_ports.set(ContentAddressableList(component_type=OutputPort), + override=True) # If ObjectiveMechanism is specified, instantiate it and OUTCOME InputPort that receives projection from it if self.objective_mechanism: diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 30dfb9ca6ea..8f6b084a1d5 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1012,12 +1012,12 @@ class OptimizationControlMechanism(ControlMechanism): Arguments --------- - state_features : Mechanism, InputPort, OutputPort, Projection, dict, or list containing any of these + state_features : Mechanism, InputPort, OutputPort, Projection, numeric value, dict, or list containing any of these specifies the Components from which `state_input_ports ` receive their inputs, the `values ` of which are assigned to `state_feature_values ` and provided as input to the `agent_rep - 's `evaluate ` method. See `state_features - ` for details of specification. + 's `evaluate ` method whent it is executed. + See `state_features ` for details of specification. state_feature_functions : Function or function : default None specifies the `function ` assigned the `InputPort` in `state_input_ports @@ -1115,10 +1115,10 @@ class OptimizationControlMechanism(ControlMechanism): one of its subclasses, or it has not been assigned (None) (see `Agent Representation and Types of Optimization ` for additional details). - state_features : Dict[Node:Port] + state_features : Dict[Node:source] dictionary listing the `INPUT ` `Nodes ` of `agent_rep - ` (keys) and the specifications in the **state_features** - argument (values) of the sources of their inputs used to construct `state_input_ports + ` (keys) and the source of their inputs (values) + as specified in **state_features** (or determined automatically), and used to construct `state_input_ports `, the values of which are assigned to `state_feature_values ` and provided as input to the `agent_rep 's `evaluate ` @@ -1155,15 +1155,17 @@ class OptimizationControlMechanism(ControlMechanism): ` following the last execution of `agent_rep `. - state_dict : Dict[node:value] + state_dict : Dict[(Port, Mechanism, Composition, index)):value] dictionary containing information about the Components corresponding to the values in `state - `. Keys are (`Port`, `Mechanism`, `Composition`) tuples, and values are - the corresponding values in `state `. The initial entries are for the - OptimizationControlMechanism's `state features `, that are the - sources of its `state_feature_values `; they are - followed by entries for the parameters modulated by the OptimizationControlMechanism's `control_signals - ` using the corresponding values of its `control_allocations - `. + `. Keys are (`Port`, `Mechanism`, `Composition`, index) tuples, + identifying the source of the value for each item at the corresponding index in + `state `, and values are its value in `state + `. The initial entries are for the OptimizationControlMechanism's + `state features `, that are the sources of its + `state_feature_values `; they are followed + by entries for the parameters modulated by the OptimizationControlMechanism's `control_signals + ` with the corresponding `control_allocation + ` values. num_estimates : int determines the number independent runs of `agent_rep ` (i.e., calls to @@ -1336,12 +1338,6 @@ class Parameters(ControlMechanism.Parameters): :default value: None :type: - state_feature_functions - see `state_feature_functions ` - - :default value: None - :type: - function see `function ` @@ -1367,6 +1363,12 @@ class Parameters(ControlMechanism.Parameters): :default value: None :type: + outcome_input_ports_option + see `outcome_input_ports_option ` + + :default value: None + :type: + saved_samples see `saved_samples ` @@ -1396,8 +1398,28 @@ class Parameters(ControlMechanism.Parameters): :default value: None :type: + + state_features + see `state_features ` + + :default value: None + :type: ``dict`` + + state_feature_functions + see `state_feature_functions ` + + :default value: None + :type: + + state_input_ports + see `state_input_ports ` + + :default value: None + :type: ``list`` """ outcome_input_ports_option = Parameter(CONCATENATE, stateful=False, loggable=False, structural=True) + state_input_ports = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) + # state_features = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) function = Parameter(GridSearch, stateful=False, loggable=False) state_feature_functions = Parameter(None, reference=True, stateful=False, loggable=False) search_function = Parameter(None, stateful=False, loggable=False) @@ -1476,7 +1498,8 @@ def __init__(self, kwargs.pop('feature_function') continue - self.state_features = state_features if isinstance(state_features, dict) else convert_to_list(state_features) + self.state_feature_specs = (state_features if isinstance(state_features, dict) + else convert_to_list(state_features)) function = function or GridSearch @@ -1499,6 +1522,7 @@ def __init__(self, super().__init__( function=function, + state_features=state_features, state_feature_functions=state_feature_functions, num_estimates=num_estimates, num_trials_per_estimate = num_trials_per_estimate, @@ -1596,7 +1620,7 @@ def _instantiate_input_ports(self, context=None): # FIX: 11/3/21 : # ADD CHECK IN _parse_state_feature_specs THAT IF A NODE RATHER THAN InputPort IS SPECIFIED, # ITS PRIMARY IS USED (SEE SCRATCH PAD FOR EXAMPLES) - if not self.state_features: + if not self.state_feature_specs: # If agent_rep is CompositionFunctionApproximator, warn if no state_features specified. # Note: if agent rep is Composition, state_input_ports and any state_feature_functions specified # are assigned in _update_state_input_ports_for_controller. @@ -1605,7 +1629,7 @@ def _instantiate_input_ports(self, context=None): else: # Implement any specified state_features - state_input_ports_specs = self._parse_state_feature_specs(self.state_features, + state_input_ports_specs = self._parse_state_feature_specs(self.state_feature_specs, self.state_feature_functions) # Note: # if state_features were specified and agent_rep is a CompositionFunctionApproximator, @@ -1618,8 +1642,9 @@ def _instantiate_input_ports(self, context=None): start = self.num_outcome_input_ports # FIX: 11/3/21 NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED stop = start + len(state_input_ports_specs) if state_input_ports_specs else 0 # FIX 11/3/21: THIS SHOULD BE MADE A PARAMETER - self.state_input_ports = ContentAddressableList(component_type=InputPort, - list=self.input_ports[start:stop]) + self.parameters.state_input_ports.set(ContentAddressableList(component_type=InputPort, + list=self.input_ports[start:stop]), + override=True) # Ensure that every state_input_port has no more than one afferent projection # FIX: NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED @@ -1662,11 +1687,9 @@ def _update_state_input_ports_for_controller(self, context=None): are specified or only one state_function is specified; otherwise, use dict for specifications). """ - # FIX: 11/15/21 - REPLACE WITH ContextFlags.PROCESSING ?? - # TRY TESTS WITHOUT THIS - # Don't instantiate unless being called by Composition.run() (which does not use ContextFlags.METHOD) + # Don't instantiate unless being called by Composition.run() # This avoids error messages if called prematurely (i.e., before run is complete) - if context.flags & ContextFlags.METHOD: + if context.flags & ContextFlags.PROCESSING: return # Don't bother for agent_rep that is not a Composition, since state_input_ports specified can be validated @@ -1676,117 +1699,8 @@ def _update_state_input_ports_for_controller(self, context=None): if self.agent_rep_type != COMPOSITION: return - from psyneulink.core.compositions.composition import \ - Composition, CompositionInterfaceMechanism, CompositionError, RunError, NodeRole - - if self.state_features: - # Validate state_features, and instantiate any that are not shadowing nodes - # Shadowing nodes are instantiated in Composition._update_shadow_projections() - comp = self.agent_rep - - if isinstance(self.state_features, list): - # Convert list to dict, assuming list is in order of INPUT Nodes, - # and assigning the corresponding INPUT Nodes as keys for use in comp._build_predicted_inputs_dict() - input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) - if len(self.state_features) > len(input_nodes): - raise OptimizationControlMechanismError( - f"The number of 'state_features' specified for {self.name} ({len(self.state_features)}) " - f"is more than the number of INPUT Nodes ({len(input_nodes)}) of the Composition assigned " - f"as its {AGENT_REP} ('{self.agent_rep.name}').") - input_dict = {} - for i, spec in enumerate(self.state_features): - input_dict[input_nodes[i]] = spec - self.state_features = input_dict - if isinstance(self.state_features, dict): - # If dict is specified, get values for checks below - state_features = list(self.state_features.values()) - assert isinstance(self.state_features, dict) - - # FIX: 1/14/21: MOVE ALL OF THIS TO ITS OWN METHOD: _validate_state_feature_specs: - - # Include agent rep in error messages if it is not the same as self.composition - self_has_state_features_str = f"'{self.name}' has 'state_features' specified " - agent_rep_str = ('' if self.agent_rep == self.composition - else f"both its `{AGENT_REP}` ('{self.agent_rep.name}') as well as ") - not_in_comps_str = f"that are missing from {agent_rep_str}'{self.composition.name}' and any " \ - f"{Composition.componentCategory}s nested within it." - - # Ensure that all InputPorts shadowed by specified state_input_ports - # are in agent_rep or one of its nested Compositions - invalid_state_features = [input_port for input_port in self.state_input_ports - if (input_port.shadow_inputs - and not (input_port.shadow_inputs.owner in - list(comp.nodes) + [n[0] for n in comp._get_nested_nodes()]) - and (not [input_port.shadow_inputs.owner.composition is x for x in - comp._get_nested_compositions() - if isinstance(input_port.shadow_inputs.owner, - CompositionInterfaceMechanism)]))] - # Ensure any Projections received from output_ports are from Nodes in agent_rep or its nested Compositions - for input_port in self.state_input_ports: - if input_port.shadow_inputs: - continue - try: - all(comp._get_source(p) for p in input_port.path_afferents) - except CompositionError: - invalid_state_features.append(input_port) - if any(invalid_state_features): - raise OptimizationControlMechanismError( - self_has_state_features_str + f"({[d.name for d in invalid_state_features]}) " + not_in_comps_str) - - # Ensure that all InputPorts shadowed by specified state_input_ports - # reference INPUT Nodes of agent_rep or of a nested Composition - invalid_state_features = [input_port for input_port in self.state_input_ports - if (input_port.shadow_inputs - and not (input_port.shadow_inputs.owner - in self._get_agent_rep_input_nodes()) - and (isinstance(input_port.shadow_inputs.owner, - CompositionInterfaceMechanism) - and not (input_port.shadow_inputs.owner.composition in - [nested_comp for nested_comp in comp._get_nested_compositions() - if nested_comp in comp.get_nodes_by_role(NodeRole.INPUT)])))] - if any(invalid_state_features): - raise OptimizationControlMechanismError( - self_has_state_features_str + f"({[d.name for d in invalid_state_features]}) " + not_in_comps_str) - - try: - # Test if state_features specified are compatible with inputs format for agent_rep Composition - # FIX: 1/10/22 - ?USE self.agent_rep.external_input_values FOR CHECK? - # Note: if state_features is a dict, keys are used in _build_predicc_inputs to identify INPUT Nodes - inputs = self.agent_rep._build_predicted_inputs_dict(None, self) - inputs_dict, num_inputs = self.agent_rep._parse_input_dict(inputs) - if len(self.state_input_ports) < len(inputs_dict): - warnings.warn(f"The 'state_features' specified for '{self.name}' are legal, but there are fewer " - f"than the number of input_nodes for its {AGENT_REP} ('{self.agent_rep.name}'); " - f"the remaining inputs will be assigned default values. Use the {AGENT_REP}'s " - f"get_inputs_format() method to see the format for its inputs.") - except RunError as error: - raise OptimizationControlMechanismError( - f"The 'state_features' argument has been specified for '{self.name}' that is using a " - f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but " - f"they are not compatible with the inputs required by its 'agent_rep': '{error.error_value}' " - f"Use the get_inputs_format() method of '{self.agent_rep.name}' to see the required format, or " - f"remove the specification of 'state_features' from the constructor for {self.name} " - f"to have them automatically assigned.") - except KeyError as error: # This occurs if a Node is illegal for a reason other than above, - pass # and will issue the corresponding error message. - except: # Legal Node specifications, but incorrect for input to agent_rep - specs = [f.full_name if hasattr(f, 'full_name') else (f.name if isinstance(f, Component) else f) - for f in state_features] - raise OptimizationControlMechanismError( - f"The 'state_features' argument has been specified for '{self.name}' that is using a " - f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but the " - f"'state_features' ({specs}) specified are not compatible with the inputs required by 'agent_rep' " - f"when it is executed. Use its get_inputs_format() method to see the required format, " - f"or remove the specification of 'state_features' from the constructor for {self.name} " - f"to have them automatically assigned.") - - # warnings.warn(f"The 'state_features' argument has been specified for '{self.name}', that is being " - # f"configured to use a {Composition.componentType} ('{self.agent_rep.name}') as its " - # f"'{AGENT_REP}'). This overrides automatic assignment of its 'state_features' as inputs to " - # f"'{self.agent_rep.name}' when it is executed. If they are not properly configured, it " - # f"will cause an error. Remove this specification from the constructor for '{self.name}' to " - # f"automatically configure its 'state_features' to be the external inputs to " - # f"'{self.agent_rep.name}'.") + if self.state_feature_specs: + self._validate_state_features() return else: @@ -1805,7 +1719,7 @@ def _update_state_input_ports_for_controller(self, context=None): state_input_ports_to_add = [] # for input_port in input_ports_not_specified: for input_port in shadow_input_ports: - input_port_name = f"{SHADOW_INPUT_NAME} of {input_port.owner.name}[{input_port.name}]" + input_port_name = f"{SHADOW_INPUT_NAME}{input_port.owner.name}[{input_port.name}]" params = {SHADOW_INPUTS: input_port, INTERNAL_ONLY:True} # Note: state_feature_functions has been validated _validate_params @@ -1823,6 +1737,119 @@ def _update_state_input_ports_for_controller(self, context=None): context=local_context) self.state_input_ports.extend(state_input_ports_to_add) + # input_values = self.state_input_ports.values + # input_nodes = self.composition._build_predicted_inputs_dict(input_values) + # sources = [self.composition._get_source(n.path_afferents[0]) for n in self.state_input_ports] + # + # # MODIFIED 1/15 OLD: + # # FIX: NEEDS TO USE _get_source() for INPUT nodes + # # self.state_features = self.composition._parse_input_dict(input_nodes) + # self.state_features = {k:v for k,v in zip(input_nodes, sources)} + # # # MODIFIED 1/15 NEW: + # # self.parameters.state_features.set(self.composition._parse_input_dict(input_nodes), + # # override=True) + # # MODIFIED 1/15 END + + def _validate_state_features(self): + from psyneulink.core.compositions.composition import \ + Composition, CompositionInterfaceMechanism, CompositionError, RunError, NodeRole + + # Validate state_features, and instantiate any that are not shadowing nodes + # Shadowing nodes are instantiated in Composition._update_shadow_projections() + comp = self.agent_rep + + if isinstance(self.state_feature_specs, list): + # Convert list to dict, assuming list is in order of INPUT Nodes, + # and assigning the corresponding INPUT Nodes as keys for use in comp._build_predicted_inputs_dict() + input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) + if len(self.state_feature_specs) > len(input_nodes): + raise OptimizationControlMechanismError( + f"The number of 'state_features' specified for {self.name} ({len(self.state_feature_specs)}) " + f"is more than the number of INPUT Nodes ({len(input_nodes)}) of the Composition assigned " + f"as its {AGENT_REP} ('{self.agent_rep.name}').") + input_dict = {} + for i, spec in enumerate(self.state_feature_specs): + input_dict[input_nodes[i]] = spec + state_features = self.state_feature_specs + elif isinstance(self.state_feature_specs, dict): + # If dict is specified, get values for checks below + state_features = list(self.state_feature_specs.values()) + + # Include agent rep in error messages if it is not the same as self.composition + self_has_state_features_str = f"'{self.name}' has 'state_features' specified " + agent_rep_str = ('' if self.agent_rep == self.composition + else f"both its `{AGENT_REP}` ('{self.agent_rep.name}') as well as ") + not_in_comps_str = f"that are missing from {agent_rep_str}'{self.composition.name}' and any " \ + f"{Composition.componentCategory}s nested within it." + + # Ensure that all InputPorts shadowed by specified state_input_ports + # are in agent_rep or one of its nested Compositions + invalid_state_features = [input_port for input_port in self.state_input_ports + if (input_port.shadow_inputs + and not (input_port.shadow_inputs.owner in + list(comp.nodes) + [n[0] for n in comp._get_nested_nodes()]) + and (not [input_port.shadow_inputs.owner.composition is x for x in + comp._get_nested_compositions() + if isinstance(input_port.shadow_inputs.owner, + CompositionInterfaceMechanism)]))] + # Ensure any Projections received from output_ports are from Nodes in agent_rep or its nested Compositions + for input_port in self.state_input_ports: + if input_port.shadow_inputs: + continue + try: + all(comp._get_source(p) for p in input_port.path_afferents) + except CompositionError: + invalid_state_features.append(input_port) + if any(invalid_state_features): + raise OptimizationControlMechanismError( + self_has_state_features_str + f"({[d.name for d in invalid_state_features]}) " + not_in_comps_str) + + # Ensure that all InputPorts shadowed by specified state_input_ports + # reference INPUT Nodes of agent_rep or of a nested Composition + invalid_state_features = [input_port for input_port in self.state_input_ports + if (input_port.shadow_inputs + and not (input_port.shadow_inputs.owner + in self._get_agent_rep_input_nodes()) + and (isinstance(input_port.shadow_inputs.owner, + CompositionInterfaceMechanism) + and not (input_port.shadow_inputs.owner.composition in + [nested_comp for nested_comp in comp._get_nested_compositions() + if nested_comp in comp.get_nodes_by_role(NodeRole.INPUT)])))] + if any(invalid_state_features): + raise OptimizationControlMechanismError( + self_has_state_features_str + f"({[d.name for d in invalid_state_features]}) " + not_in_comps_str) + + # Ensure state_features are compatible with input format for agent_rep Composition + try: + # FIX: 1/10/22 - ?USE self.agent_rep.external_input_values FOR CHECK? + inputs = self.agent_rep._build_predicted_inputs_dict(None, self) + inputs_dict, num_inputs = self.agent_rep._parse_input_dict(inputs) + if len(self.state_input_ports) < len(inputs_dict): + warnings.warn(f"The 'state_features' specified for '{self.name}' are legal, but there are fewer " + f"than the number of input_nodes for its {AGENT_REP} ('{self.agent_rep.name}'); " + f"the remaining inputs will be assigned default values. Use the {AGENT_REP}'s " + f"get_inputs_format() method to see the format for its inputs.") + except RunError as error: + raise OptimizationControlMechanismError( + f"The 'state_features' argument has been specified for '{self.name}' that is using a " + f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but " + f"they are not compatible with the inputs required by its 'agent_rep': '{error.error_value}' " + f"Use the get_inputs_format() method of '{self.agent_rep.name}' to see the required format, or " + f"remove the specification of 'state_features' from the constructor for {self.name} " + f"to have them automatically assigned.") + except KeyError as error: # This occurs if a Node is illegal for a reason other than above, + pass # and will issue the corresponding error message. + except: # Legal Node specifications, but incorrect for input to agent_rep + specs = [f.full_name if hasattr(f, 'full_name') else (f.name if isinstance(f, Component) else f) + for f in state_features] + raise OptimizationControlMechanismError( + f"The 'state_features' argument has been specified for '{self.name}' that is using a " + f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but the " + f"'state_features' ({specs}) specified are not compatible with the inputs required by 'agent_rep' " + f"when it is executed. Use its get_inputs_format() method to see the required format, " + f"or remove the specification of 'state_features' from the constructor for {self.name} " + f"to have them automatically assigned.") + def _instantiate_output_ports(self, context=None): """Assign CostFunctions.DEFAULTS as default for cost_option of ControlSignals. """ @@ -2507,7 +2534,6 @@ def _get_agent_rep_input_nodes(self, comp=None): input_nodes.append(node) return input_nodes - def _parse_state_feature_function(self, feature_function): if isinstance(feature_function, Function): return copy.deepcopy(feature_function) @@ -2525,7 +2551,7 @@ def _parse_state_feature_specs(self, state_features, feature_functions, context= input_node_names = [n.name if n else None for n in self._get_agent_rep_input_nodes()] input_port_names = None if isinstance(state_features, dict): - if SHADOW_INPUTS in self.state_features: + if SHADOW_INPUTS in self.state_feature_specs: pass # handled below else: input_port_names = [k.name for k in list(state_features.keys())] @@ -2590,6 +2616,14 @@ def num_state_input_ports(self): except: return 0 + @property + def state_features(self): + from psyneulink.core.compositions.composition import NodeRole + input_nodes = self.composition.get_nodes_by_role(NodeRole.INPUT) + sources = [source_tuple[0] if source_tuple[0] != DEFAULT_VARIABLE else value + for source_tuple,value in list(self.state_dict.items())[:len(self.state_input_ports)]] + return {k:v for k,v in zip(input_nodes, sources)} + @property def state(self): state_feature_values = (self.state_feature_values if len(self.state_feature_values) @@ -2597,19 +2631,17 @@ def state(self): # return np.append(state_feature_values, self.control_allocation, 0) return [v.tolist() for v in state_feature_values] + self.control_allocation.tolist() - # FIX: 1/6/22 - FINISH IMPLEMENTING: - # - ADD ENTRIES FOR ALL NODES THAT CONTRIBUTE TO STATE_INPUT_PORTS, EVEN IF CONVERGENT - # - ADD ENTRIES FOR ALL NODES MODULATED BY CONTROL_SIGNAL EVEN IF DIVERGENT - # - DEAL WITH CONTROL_SIGNALS THAT PROJECT TO NESTED NODES (GET METHOD FROM parameter_CIM) - # - MODIFY KEYS TO BE (NODE, PORT) TUPLE - # - DOCUMENT CHANGE TO KEYS UNDER ATTRIBUTES (state_dict : ) @property def state_dict(self): """Return dict with (node, port, Composition, index) tuples as keys and corresponding state[index] as values. Note: the index is required, since a state_input_port may have more than one afferent Projection - (that is, a state_feature_value may be determined by more than one node), + (that is, a state_feature_value may be determined by Projections from more than one Node), and a ControlSignal may have more than one ControlProjection (that is, a given element of the - control_allocation may apply to more than one Parameter). + control_allocation may apply to more than one Parameter). However, for state_input_ports that shadow + a Node[InputPort], only that Node[InputPort] is listed in state_dict even if the Node[InputPort] being + shadowed has more than one afferent Projection (this is because it is the value of the Node[InputPort] + (after it has processed the value of its afferent Projections) that determines the input to the + state_input_port. """ state_dict = {} @@ -2627,7 +2659,6 @@ def state_dict(self): get_info_method = composition._get_destination if not port.path_afferents: if port.default_input is DEFAULT_VARIABLE: - # FIX: 1/14/22 DOUBLECHECK THAT THE FOLLWING IS CORRECT: source_port = DEFAULT_VARIABLE node = None comp = None @@ -2637,19 +2668,6 @@ def state_dict(self): else: source_port, node, comp = get_info_method(port.path_afferents[0]) state_dict.update({(source_port, node, comp, state_index):self.state[state_index]}) - # # MODIFIED 1/8/22 ALT: SEPARATELY LISTS OUTPUT_PORTS THAT PROJECT TO SAME SHADOWED INPUT_PORT - # if port.shadow_inputs: - # port = port.shadow_inputs - # if port.owner in self.composition.nodes: - # composition = self.composition - # get_info_method = composition._get_source - # else: - # composition = port.path_afferents[0].sender.owner.composition - # get_info_method = composition._get_destination - # for projection in port.path_afferents: - # source_port, node, comp = get_info_method(projection) - # state_dict.update({(source_port, node, comp, state_index):self.state[state_index]}) - # MODIFIED 1/8/22 END state_index += 1 # Get recipients of control_allocations values of state: diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 1c98b004d82..c2a66e9ccb3 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -8038,12 +8038,9 @@ def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): controller = controller or self.controller # Use keys for inputs dict from OptimizationControlMechanism state_features if it is specified as a dict # (unless it has SHADOW_INPUTS entry, in which case that is handled below) - input_dict_keys = (list(controller.state_features.keys()) - if (hasattr(controller, STATE_FEATURES) - and isinstance(controller.state_features, dict) - and SHADOW_INPUTS not in controller.state_features) - else None) + input_dict_keys = controller.agent_rep.get_nodes_by_role(NodeRole.INPUT)[:len(controller.state_input_ports)] inputs = {} + no_predicted_input = (predicted_inputs is None or not len(predicted_inputs)) if no_predicted_input: warnings.warn(f"{self.name}.evaluate() called without any inputs specified; default values will be used") diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index a75dad0b5fa..c78f5a146ce 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -765,6 +765,7 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c state_feature_specs = ['partial_legal_ports_spec', 'full_legal_ports_spec', 'input_dict_spec', + 'automatic_assignment', 'shadow_inputs_dict_spec', 'misplaced_shadow', 'ext_shadow', @@ -774,15 +775,16 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ] state_feature_args = [ - (state_feature_specs[0], messages[0], UserWarning), - (state_feature_specs[1], None, None), - (state_feature_specs[2], None, None), - (state_feature_specs[3], None, None), - (state_feature_specs[4], messages[1], pnl.CompositionError), - (state_feature_specs[5], messages[2], pnl.OptimizationControlMechanismError), - (state_feature_specs[6], messages[3], pnl.OptimizationControlMechanismError), - (state_feature_specs[7], messages[4], pnl.OptimizationControlMechanismError), - (state_feature_specs[8], messages[5], pnl.OptimizationControlMechanismError) + (state_feature_specs[0], messages[0], UserWarning), # partial_legal_ports_spec + (state_feature_specs[1], None, None), # full_legal_ports_spec + (state_feature_specs[2], None, None), # input_dict_spec + (state_feature_specs[3], None, None), # automatic_assignment + (state_feature_specs[4], None, None), # shadow_inputs_dict_spec + (state_feature_specs[5], messages[1], pnl.CompositionError), # misplaced_shadow + (state_feature_specs[6], messages[2], pnl.OptimizationControlMechanismError),# ext_shadow + (state_feature_specs[7], messages[3], pnl.OptimizationControlMechanismError),# ext_output_port + (state_feature_specs[8], messages[4], pnl.OptimizationControlMechanismError),# bad_input_format_spec_wrong_shape + (state_feature_specs[9], messages[5], pnl.OptimizationControlMechanismError) # bad_input_format_spec_too_many ] @pytest.mark.control @@ -803,6 +805,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'partial_legal_ports_spec': [oa.output_port], 'full_legal_ports_spec': [ia.input_port, oa.output_port, [3,1,2]], 'input_dict_spec': {icomp:ia.input_port, oa:oc.input_port, ob:ob.output_port}, + 'automatic_assignment': None, 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, 'misplaced_shadow':ib.input_port, 'ext_shadow':ext.input_port, @@ -819,29 +822,38 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg allocation_samples=[10, 20, 30]), pnl.ControlSignal(modulates=(pnl.INTERCEPT,oc), allocation_samples=[10, 20, 30])]) - if not state_feature_args[1]: ocomp.add_controller(ocm) ocomp.run() - if state_feature_args[0] == 'partial_legal_ports_spec': - assert len(ocm.state_input_ports) == 1 - assert ocm.state_input_ports.names == ['OA[OutputPort-0]'] - - elif state_feature_args[0] == 'full_legal_ports_spec': + if state_feature_args[0] == 'full_legal_ports_spec': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA', 'OA[OutputPort-0]', 'OB'] + assert ocm.state_features == {icomp: ia.input_port, oa: oa.output_port, ob: [3, 1, 2]} elif state_feature_args[0] == 'input_dict_spec': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['INNER COMP', 'OA', 'OB'] + assert ocm.state_features == {icomp:ia.input_port, oa:oc.input_port, ob:ob.output_port} + + elif state_feature_args[0] == 'automatic_assignment': + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OA[InputPort-0]', + 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_features == {icomp: ia.input_port, oa: oa.input_port, ob: ob.input_port} elif state_feature_args[2] is UserWarning: with pytest.warns(UserWarning) as warning: ocomp.add_controller(ocm) ocomp.run() + if state_feature_args[0] == 'partial_legal_ports_spec': + assert len(ocm.state_input_ports) == 1 + assert ocm.state_input_ports.names == ['OA[OutputPort-0]'] + assert ocm.state_features == {icomp: oa.output_port} assert warning[10].message.args[0] == message + assert ocm.state_features == {icomp: oa.output_port} else: with pytest.raises(state_feature_args[2]) as error: diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index d92c6bf1d83..108a5a937d1 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -246,7 +246,7 @@ def test_converging_pathways(self): ), ( {'show_controller': True, 'show_node_structure': True}, - '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 style=solid]\n\t"my ocm":"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"my ocm":"OutputPort-ib[slope] ControlSignal" -> ib:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> "my ocm":"InputPort-Shadowed input of of ia[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + '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 style=solid]\n\t"my ocm":"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"my ocm":"OutputPort-ib[slope] ControlSignal" -> ib:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> "my ocm":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ) ] @@ -399,19 +399,19 @@ def test_nested_learning(self, show_graph_kwargs, expected_output): ), ( {'show_nested': False, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ] @@ -509,12 +509,12 @@ def test_nested_learning_test_with_user_specified_target_in_outer_composition( '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', - '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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of ob[InputPort-0]Shadowed input of of oa[InputPort-0]
> 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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}' + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> 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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}' ] @pytest.mark.parametrize( @@ -563,12 +563,12 @@ def test_of_show_nested_show_cim_and_show_node_structure( '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT_CIM" -> ma [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\ticomp -> "midcomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"midcomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT_CIM" -> ma [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"icomp OUTPUT_CIM" -> "midcomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> icomp [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', - '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_CIM":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"midcomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> "midcomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> icomp [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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_CIM":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"midcomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> "midcomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', ] @pytest.mark.parametrize( @@ -622,11 +622,11 @@ def test_of_show_3_level_nested_show_cim_and_show_node_structure( '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of of oa[InputPort-0]Shadowed input of of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', # FIX: NEEDS TO BE CORRECTED ONCE BUG IS FIXED (SEE MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c) # '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/models/test_greedy_agent.py b/tests/models/test_greedy_agent.py index 8a75534e408..676150e3e1d 100644 --- a/tests/models/test_greedy_agent.py +++ b/tests/models/test_greedy_agent.py @@ -192,7 +192,8 @@ def action_fn(variable): agent_comp.exclude_node_roles(direct_move, NodeRole.OUTPUT) - ocm = OptimizationControlMechanism(state_features={SHADOW_INPUTS: [player_pos, predator_pos, prey_pos]}, + ocm = OptimizationControlMechanism(state_features=[player_pos, prey_pos, predator_pos], + # ocm = OptimizationControlMechanism(state_features={SHADOW_INPUTS: [player_pos, prey_pos, predator_pos]}, agent_rep=agent_comp, function=GridSearch(direction=MINIMIZE, save_values=True), From 1bc3eca5a83369805c606f0a55d60a6d69b9d84f Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 14 Jan 2022 19:26:21 -0500 Subject: [PATCH 104/285] llvm, ports: Rename state_f -> port_f. States were renamed to ports long time ago. Signed-off-by: Jan Vesely --- psyneulink/core/components/ports/port.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index bf5a834cce8..e8e6d057a41 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -2284,12 +2284,12 @@ def _get_input_struct_type(self, ctx): return pnlvm.ir.LiteralStructType(input_types) def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, *, tags:frozenset): - state_f = ctx.import_llvm_function(self.function) + port_f = ctx.import_llvm_function(self.function) # Create a local copy of the function parameters base_params = pnlvm.helpers.get_param_ptr(builder, self, params, "function") - f_params = builder.alloca(state_f.args[0].type.pointee) + f_params = builder.alloca(port_f.args[0].type.pointee) builder.store(builder.load(base_params), f_params) # FIXME: Handle and combine multiple afferents @@ -2338,13 +2338,13 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, builder.store(param_val, f_mod_param_ptr) # OutputPort returns 1D array even for scalar functions - if arg_out.type != state_f.args[3].type: + if arg_out.type != port_f.args[3].type: assert len(arg_out.type.pointee) == 1 arg_out = builder.gep(arg_out, [ctx.int32_ty(0), ctx.int32_ty(0)]) # Extract the data part of input f_input = builder.gep(arg_in, [ctx.int32_ty(0), ctx.int32_ty(0)]) f_state = pnlvm.helpers.get_state_ptr(builder, self, state, "function") - builder.call(state_f, [f_params, f_state, f_input, arg_out]) + builder.call(port_f, [f_params, f_state, f_input, arg_out]) return builder @staticmethod From 9f7e4e803eaa7cc8b98a222598d4f1157badcdfc Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 22 Jan 2022 14:30:15 -0500 Subject: [PATCH 105/285] llvm/codegen/UDF: Add human readable name for stack slots for local variables Signed-off-by: Jan Vesely --- psyneulink/core/llvm/codegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index d80a2faa4c4..1425e915070 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -238,7 +238,7 @@ def visit_Assign(self, node): for t in node.targets: target = self.visit(t) if target is None: # Allocate space for new variable - target = self.var_builder.alloca(value.type) + target = self.var_builder.alloca(value.type, name=str(t.id) + '_local_variable') self.register[t.id] = target assert self.is_lval(target) self.builder.store(value, target) From 40b1f98a6d779004022bb89c87164780c479afcc Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 23 Jan 2022 15:25:11 -0500 Subject: [PATCH 106/285] llvm/builder_context: Drop special handling of NUM_ESTIMATES parameter The parameter is not used in compiled code. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/builder_context.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index 48516a81516..1090171f7bb 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -354,8 +354,6 @@ def _param_struct(p): return ir.LiteralStructType(self.get_param_struct_type(x) for x in val) elif p.name == 'matrix': # Flatten matrix val = np.asfarray(val).flatten() - elif p.name == NUM_ESTIMATES: # Should always be int - val = np.int32(0) if val is None else np.int32(val) elif p.name == 'num_trials_per_estimate': # Should always be int val = np.int32(0) if val is None else np.int32(val) elif np.ndim(val) == 0 and component._is_param_modulated(p): From ccfce23246e460ea654671478e658405ddeda6b8 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 23 Jan 2022 15:32:07 -0500 Subject: [PATCH 107/285] llvm/codegen: Add extra checks when selecting PRNG functions Signed-off-by: Jan Vesely --- psyneulink/core/llvm/builder_context.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index 1090171f7bb..23ab4da929d 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -179,15 +179,20 @@ def init_builtins(self): def get_uniform_dist_function_by_state(self, state): if len(state.type.pointee) == 5: return self.import_llvm_function("__pnl_builtin_mt_rand_double") - if len(state.type.pointee) == 7: + elif len(state.type.pointee) == 7: + # we have different versions based on selected FP precision return self.import_llvm_function("__pnl_builtin_philox_rand_{}".format(str(self.float_ty))) + else: + assert False, "Unknown PRNG type!" def get_normal_dist_function_by_state(self, state): if len(state.type.pointee) == 5: return self.import_llvm_function("__pnl_builtin_mt_rand_normal") - if len(state.type.pointee) == 7: + elif len(state.type.pointee) == 7: # Normal exists only for self.float_ty return self.import_llvm_function("__pnl_builtin_philox_rand_normal") + else: + assert False, "Unknown PRNG type!" def get_builtin(self, name: str, args=[], function_type=None): if name in _builtin_intrinsics: @@ -272,6 +277,8 @@ def get_random_state_ptr(self, builder, component, state, params): reseed_f = self.get_builtin("mt_rand_init") elif seed_idx == 6: reseed_f = self.get_builtin("philox_rand_init") + else: + assert False, "Unknown PRNG type!" builder.call(reseed_f, [random_state_ptr, new_seed]) From 027f796117edd1f3ba880026d36564b8744a0e33 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 23 Jan 2022 15:41:10 -0500 Subject: [PATCH 108/285] llvm, functions/UDF: Fix code generation for Python pow operator This never worked, the test was testing division instead. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/codegen.py | 6 +++--- tests/functions/test_user_defined_func.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index 1425e915070..fb780a6a50f 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -163,13 +163,13 @@ def _div(builder, x, y): return _div def visit_Pow(self, node): - def _div(builder, x, y): + def _pow(builder, x, y): assert helpers.is_floating_point(x) assert helpers.is_floating_point(y) - pow_f = ctx.get_builtin("pow", [x.type, y.type]) + pow_f = self.ctx.get_builtin("pow", [x.type, y.type]) return builder.call(pow_f, [x, y]) - return _div + return _pow def visit_USub(self, node): def _usub(builder, x): diff --git a/tests/functions/test_user_defined_func.py b/tests/functions/test_user_defined_func.py index 5deae846a06..d6786284fde 100644 --- a/tests/functions/test_user_defined_func.py +++ b/tests/functions/test_user_defined_func.py @@ -31,7 +31,7 @@ def binDiv(_, param1, param2): def binPow(_, param1, param2): - return param1 / param2 + return param1 ** param2 @pytest.mark.parametrize("param1", [1, np.ones(2), np.ones((2, 2))], ids=['scalar', 'vector', 'matrix']) From 537d03e99f1e146f71e7c2186700eb447effc5c2 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 22 Jan 2022 15:23:48 -0500 Subject: [PATCH 109/285] llvm, component: Do not include 'allocation_samples' param in compiled params The values are duplicated in 'search_space' param of OCM. Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 548fe2cfdd8..c9b3783ddc2 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1382,7 +1382,9 @@ def _get_compilation_params(self): "auto", "hetero", "cost", "costs", "combined_costs", "control_signal", # autodiff specific types - "pytorch_representation", "optimizer"} + "pytorch_representation", "optimizer", + # duplicate + "allocation_samples"} # Mechanism's need few extra entires: # * matrix -- is never used directly, and is flatened below # * integration rate -- shape mismatch with param port input From 97d07ff85f1d1ddd1b23dcb787a1709f26ca3ee4 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 22 Jan 2022 23:40:12 -0500 Subject: [PATCH 110/285] llvm, component: Block num_executions from all components other than Mechanisms and Compositions It's not used in compiled code for any other component, and takes too much space. This saves ~4kB of state for Predator-Prey model. Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index c9b3783ddc2..ceb55a55a13 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1320,6 +1320,11 @@ def _get_compilation_state(self): # Only mechanisms use "value" state if not hasattr(self, 'ports'): blacklist.add("value") + + # Only mechanisms and compositions need 'num_executions' + if not hasattr(self, 'ports') and not hasattr(self, 'nodes'): + blacklist.add("num_executions") + def _is_compilation_state(p): #FIXME: This should use defaults instead of 'p.get' return p.name not in blacklist and \ From a271d90d5572125174fc0bc726f1016a284e71e8 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Mon, 24 Jan 2022 15:16:57 -0500 Subject: [PATCH 111/285] llvm, component: Do not include 'control_allocation_search_space' param in compiled params This duplicates the information in 'search_space' param. Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index ceb55a55a13..fa1207ce08e 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1389,7 +1389,7 @@ def _get_compilation_params(self): # autodiff specific types "pytorch_representation", "optimizer", # duplicate - "allocation_samples"} + "allocation_samples", "control_allocation_search_space"} # Mechanism's need few extra entires: # * matrix -- is never used directly, and is flatened below # * integration rate -- shape mismatch with param port input From c58ad068c00715de0f0f08d9467ff6c5f7b48e17 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Mon, 24 Jan 2022 23:56:50 -0500 Subject: [PATCH 112/285] llvm, functions/AccumulatorIntegrator: Add compilation support (#2294) --- .../functions/stateful/integratorfunctions.py | 24 +++++++++++++ tests/functions/test_integrator.py | 35 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/psyneulink/core/components/functions/stateful/integratorfunctions.py b/psyneulink/core/components/functions/stateful/integratorfunctions.py index 81a612e1b3c..e845976c1c5 100644 --- a/psyneulink/core/components/functions/stateful/integratorfunctions.py +++ b/psyneulink/core/components/functions/stateful/integratorfunctions.py @@ -666,6 +666,30 @@ def _function(self, return self.convert_output_type(value) + def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): + rate = self._gen_llvm_load_param(ctx, builder, params, index, RATE) + increment = self._gen_llvm_load_param(ctx, builder, params, index, INCREMENT) + noise = self._gen_llvm_load_param(ctx, builder, params, index, NOISE, + state=state) + + # Get the only context member -- previous value + prev_ptr = pnlvm.helpers.get_state_ptr(builder, self, state, "previous_value") + # Get rid of 2d array. When part of a Mechanism the input, + # (and output, and context) are 2d arrays. + prev_ptr = pnlvm.helpers.unwrap_2d_array(builder, prev_ptr) + assert len(prev_ptr.type.pointee) == len(vi.type.pointee) + + prev_ptr = builder.gep(prev_ptr, [ctx.int32_ty(0), index]) + prev_val = builder.load(prev_ptr) + + res = builder.fmul(prev_val, rate) + res = builder.fadd(res, noise) + res = builder.fadd(res, increment) + + vo_ptr = builder.gep(vo, [ctx.int32_ty(0), index]) + builder.store(res, vo_ptr) + builder.store(res, prev_ptr) + class SimpleIntegrator(IntegratorFunction): # ------------------------------------------------------------------------- """ diff --git a/tests/functions/test_integrator.py b/tests/functions/test_integrator.py index 78f19dabcb4..f98ca325c07 100644 --- a/tests/functions/test_integrator.py +++ b/tests/functions/test_integrator.py @@ -132,6 +132,33 @@ def LeakyFun(init, value, iterations, noise, **kwargs): 2.19281309, 1.61148745, 3.23404557, 2.81418859, 2.63042344] +def AccumulatorFun(init, value, iterations, noise, **kwargs): + assert iterations == 3 + + if np.isscalar(noise): + if "initializer" not in kwargs: + # variable is not used in Accumulator + return [[1.38631136, 1.38631136, 1.38631136, 1.38631136, 1.38631136, + 1.38631136, 1.38631136, 1.38631136, 1.38631136, 1.38631136]] + else: + return [[1.40097107, 1.39610447, 1.39682937, 1.40344986, 1.38762668, + 1.38792466, 1.38668573, 1.40172829, 1.40071984, 1.40242065]] + elif isinstance(noise, pnl.DistributionFunction): + if "initializer" not in kwargs: + return [[1.46381634, 0.97440038, 0.54931704, 0.28681701, 0.26162584, + 0.66800459, 1.1010486, 0.02587729, 0.38761176, -0.56452977]] + else: + return [[1.47847605, 0.98419348, 0.55983505, 0.30395551, 0.26294116, + 0.66961789, 1.10142297, 0.04129421, 0.40202024, -0.54842049]] + else: + if "initializer" not in kwargs: + return [[1.65907194, 1.41957474, 0.96892655, 1.39471298, 0.51090402, + 1.20706503, 0.5443729, 1.61376489, 1.04949166, 0.90644658]] + else: + return [[1.67373165, 1.42936784, 0.97944456, 1.41185147, 0.51221934, + 1.20867833, 0.54474727, 1.62918182, 1.06390014, 0.92255587]] + + GROUP_PREFIX="IntegratorFunction " @@ -149,6 +176,7 @@ def LeakyFun(init, value, iterations, noise, **kwargs): (Functions.DriftDiffusionIntegrator, DriftIntFun), (Functions.DriftOnASphereIntegrator, DriftSphereFun), (Functions.LeakyCompetingIntegrator, LeakyFun), + (Functions.AccumulatorIntegrator, AccumulatorFun), ], ids=lambda x: x[0]) @pytest.mark.benchmark def test_execute(func, func_mode, variable, noise, params, benchmark): @@ -171,6 +199,13 @@ def test_execute(func, func_mode, variable, noise, params, benchmark): if 'dimension' in params: params.pop('dimension') + if 'AccumulatorIntegrator' in func[0].componentName: + params = { + **params, + 'increment': RAND0_1, + } + params.pop('offset') + f = func[0](default_variable=variable, noise=noise, **params) ex = pytest.helpers.get_func_execution(f, func_mode) From 547d6527ec7ba51dd21be628c2776436155f63ce Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 25 Jan 2022 20:36:35 -0500 Subject: [PATCH 113/285] github-actions/install-pnl: Update Ubuntu package database before installing graphviz (#2297) Signed-off-by: Jan Vesely --- .github/actions/install-pnl/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/install-pnl/action.yml b/.github/actions/install-pnl/action.yml index d5ec8de3949..0161d0e575d 100644 --- a/.github/actions/install-pnl/action.yml +++ b/.github/actions/install-pnl/action.yml @@ -18,7 +18,7 @@ runs: run: | case "$RUNNER_OS" in macOS*) brew install graphviz ;; - Linux*) sudo apt-get install -y graphviz ;; + Linux*) sudo apt-get update && sudo apt-get install -y --no-install-recommends graphviz ;; Windows*) choco install --no-progress -y graphviz --version=2.38.0.20190211 ;; *) echo "Unsupported OS"; exit 1 ;; esac From b20732022ad53b73ff9c0ec4905a9307085aec39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jan 2022 15:28:50 +0000 Subject: [PATCH 114/285] requirements: update sphinx-autodoc-typehints requirement (#2298) --- doc_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_requirements.txt b/doc_requirements.txt index 043ea79e043..9ed5718328f 100644 --- a/doc_requirements.txt +++ b/doc_requirements.txt @@ -1,3 +1,3 @@ psyneulink-sphinx-theme<1.2.3.1 sphinx<4.2.1 -sphinx_autodoc_typehints<1.16.0 +sphinx_autodoc_typehints<1.17.0 From c8f5f89665e7abb720afb84f2ae0670417a359a3 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Thu, 27 Jan 2022 17:56:33 -0500 Subject: [PATCH 115/285] Feat/ocm/state features (#2299) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - _update_state_input_ports_for_controller: - skip if ContextFlags.METHOD -> ContextFlags.PROCESSING * • optimizationcontrolmechanism.py: - add _validate_state_features - _update_state_input_ports_for_controller: validation of state_features moved to _validate_state_features * - * • optimizationcontrolmechanism.py - Parameters: add outcome_input_ports and state_input_ports * • controlmechanism.py & optimizationcontrolmechanism.py - move outcome_input_ports Parameter from ocm to controlmechanism - state_features: set to make Parameter on ocm, but waiting to deal with LLVM * • controlmechanism.py & optimizationcontrolmechanism.py - move outcome_input_ports Parameter from ocm to controlmechanism - state_features: set to make Parameter on ocm, but waiting to deal with LLVM * Passing all tests but ones in test_show_graph and predator_prey * • test_show_graph.py: - correct output for fix of typo • test_greedy_agent.py: - test_predator_prey(): reorder args in state_features to fix failure * - * - * - * • inputport.py: - _parse_self_port_type_spec(): change name of shadowed inputports to use full_name * - * - * - * - * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): - convert list spec to dict - handle nested Comp INPUT Node in set spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): passes all tests * - * - * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): errors for comp spec in list or SHADOW_INPUTS dict (should be replaced by expansion to INPUT Nodes of comp • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add tests for comp spec in list or SHADOW_INPUTS dict (replace with checks when expansion of these is handled) * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order) *** TBI state_features needs to be modified to accomodate this *** • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add test for None in list spec * • optimizationcontrolmechanism.py state_features: use self._input_nodes_for_state_features_specs * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order) *** TBI state_features needs to be modified to accomodate this *** • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add test for None in list spec * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order): fully implemented TBI: implement for SHADOW_INPUTS list spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): passes all tests * • optimizationcontrolmechanism.py: - move warning for specs < INPUT Nodes from _validate_state_features() to _parse_state_feature_specs() * • optimizationcontrolmechanism.py: - move warning for specs < INPUT Nodes from _validate_state_features() to _parse_state_feature_specs() * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): handle None in SHADOW_INPUTS spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors: - properly handle shadow_inputs_dict_spec and shadow_inputs_dict_spec_w_none conditions * Passes all test_ocm_state_feature_specs_and_warnings_and_errors tests, but uses internal dict that fails for deferred_init of agent_rep, since INPUT nodes (used as keys in dict) may not yet have been constructed. * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): refactored to use parallel lists rather than dict for nodes and specs • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - all tests pass * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): still needs to deal with CFA * - * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • optimizationcontrolmechanism.py _parse_state_feature_specs(): - suppress warning for more state_features than INPUT Nodes in agent_rep (duplicates exception raised in _validate_state_features -- but see comment) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • composition.py - run(): add context.execution_phase = ContextFlags.PREPARING at beginning of method * - * - * MODS TO DEFER VALIDATION OF state_features UNTIL RUN TIME (AFTER Composition IS FULLY CONSTRUCTED) • composition.py: - run(): - set context.execution_phase to ContextFlags.PREPARING at start and reinstate entry context just before executing trials - _complete_init_of_partially_initialized_nodes(): - set needs_update_controller based on return value of ocm._update_state_input_ports_for_controller() • optmizationcontrolmechanism.py: _ _update_state_input_ports_for_controller(): - restrict call to _validate_state_features() to run time (ContextFlags.PROCESSING) - return True if passes, None otherwise - _validate_state_features(): - reinstate warning for number of state_features > agent_rep INPUT Nodes • test_control.py: -test_args_specific_to_ocm() - add run() to test to preciptitate error detected in _validate_state_feature_specs() - test_ocm_state_feature_specs_and_warnings_and_errors(): - reinstate 'too_many_inputs_warning' condition * - * - * • optimizationcontrolmechanism.py: deal with deferred INPUT nodes in state_features * - * - * • Passes all tests in test_ocm_state_feature_specs_and_warnings_and_errors() * - * - * • optimizationcontrolmechanism.py: - add _validate_input_nodes() - add _instantiate_pending_state_features() - STUB at present * - * - * • Passes all tests * • optimizationcontrolmechanism.py - state_features: updated after adding nodes to comp * - * • Passes all tests • optimizationcontrolmechanism.py - state_features: correct to properly include updated INPUT nodes * • GENUNINELY Passes all tests (some were commented out on the prior commit) • optimizationcontrolmechanism.py - _parse_state_feature_specs() & state_features: further fixes to properly include added INPUT nodes * - * - * - * • optimizationcontrolmechanism.py _parse_state_feature_specs() & _validate_state_features(): add mention of missing nodes to warning / error messages * • optimizationcontrolmechanism.py add _get_nodes_not_in_agent_rep() * - * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): enforce list spec for agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- .../core/components/mechanisms/mechanism.py | 21 - .../control/optimizationcontrolmechanism.py | 704 +++++++++++++----- psyneulink/core/components/ports/inputport.py | 6 +- psyneulink/core/compositions/composition.py | 29 +- tests/composition/test_control.py | 302 ++++++-- tests/composition/test_show_graph.py | 39 +- 6 files changed, 790 insertions(+), 311 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index e6feee7f200..616379ce75e 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2487,31 +2487,10 @@ def execute(self, else: if context.source & ContextFlags.COMMAND_LINE: context.execution_phase = ContextFlags.PROCESSING - if input is not None: input = convert_all_elements_to_np_array(input) - # MODIFIED 1/14/22 NEW: - else: - input = self.defaults.variable - # MODIFIED 1/14/22 END - if input is None: - # MODIFIED 1/14/22 OLD: input = self.defaults.variable - # # MODIFIED 1/14/22 NEW: Handle None on port-by-port basis (using default_input attribute) - # if 'Parameter_CIM' in self.name: - # input = self.defaults.variable - # else: - # input = self._update_input_ports(context=context) - # # MODIFIED 1/14/22 NEWER: Handle None on port-by-port basis (using default_input attribute) - # if self.path_afferents: - # input = [] - # for input_port in self.input_ports: - # if input_port.path_afferents or input_port.default_input: - # input.append(self.defaults.variable[i]) - # else: - # input.append(None) - # MODIFIED 1/14/22 END # FIX: this input value is sent to input CIMs when compositions are nested # variable should be based on afferent projections variable = self._get_variable_from_input(input, context) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 8f6b084a1d5..8ca8c99fc2c 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -400,8 +400,8 @@ COMMENT: FIX: CONFIRM THAT THE FOLLOWING ALL WORK - COMMENT State features can also be added to an existing OptimizationControlMechanism using its `add_state_features` method. + COMMENT .. _OptimizationControlMechanism_State_Feature_Functions_Arg: @@ -920,6 +920,7 @@ import copy import warnings from collections.abc import Iterable +from typing import Union import numpy as np import typecheck as tc @@ -1476,12 +1477,12 @@ def __init__(self, for k in kwargs.copy(): if k == 'features': if state_features: - warnings.warn(f"Both 'features' and 'state_features' were specified in the constructor for an" - f" {self.__class__.__name__}. Note: 'features' has been deprecated; " - f"'state_features' ({state_features}) will be used.") + warnings.warn(f"Both 'features' and '{STATE_FEATURES}' were specified in the constructor " + f"for an {self.__class__.__name__}. Note: 'features' has been deprecated; " + f"'{STATE_FEATURES}' ({state_features}) will be used.") else: warnings.warn(f"'features' was specified in the constructor for an {self.__class__.__name__}; " - f"Note: 'features' has been deprecated; please use 'state_features' in the future.") + f"Note: 'features' has been deprecated; please use '{STATE_FEATURES}' in the future.") state_features = kwargs['features'] kwargs.pop('features') continue @@ -1498,7 +1499,7 @@ def __init__(self, kwargs.pop('feature_function') continue - self.state_feature_specs = (state_features if isinstance(state_features, dict) + self.state_feature_specs = (state_features if isinstance(state_features, (dict, set)) else convert_to_list(state_features)) function = function or GridSearch @@ -1519,11 +1520,25 @@ def __init__(self, else: assert False, f"PROGRAM ERROR: 'agent_rep' arg should have been specified " \ f"in internal call to constructor for {self.name}." + elif agent_rep.componentCategory=='Composition': + from psyneulink.core.compositions.composition import NodeRole + # If there are more state_features than INPUT Nodes in agent_rep, defer initialization until they are added + if (state_features + and len(convert_to_list(state_features)) > len(agent_rep.get_nodes_by_role(NodeRole.INPUT))): + # Temporarily name InputPort + self._assign_deferred_init_name(self.__class__.__name__) + # Store args for deferred initialization + self._store_deferred_init_args(**locals()) + + # Flag for deferred initialization + self.initialization_status = ContextFlags.DEFERRED_INIT + return super().__init__( - function=function, + agent_rep=agent_rep, state_features=state_features, state_feature_functions=state_feature_functions, + function=function, num_estimates=num_estimates, num_trials_per_estimate = num_trials_per_estimate, random_variables=random_variables, @@ -1532,7 +1547,6 @@ def __init__(self, search_statefulness=search_statefulness, search_function=search_function, search_termination_function=search_termination_function, - agent_rep=agent_rep, **kwargs ) @@ -1616,6 +1630,7 @@ def _instantiate_input_ports(self, context=None): # If any state_features were specified parse them and pass to ControlMechanism._instantiate_input_ports() state_input_ports_specs = None + self._specified_input_nodes_in_order = [] # FIX: 11/3/21 : # ADD CHECK IN _parse_state_feature_specs THAT IF A NODE RATHER THAN InputPort IS SPECIFIED, @@ -1625,7 +1640,7 @@ def _instantiate_input_ports(self, context=None): # Note: if agent rep is Composition, state_input_ports and any state_feature_functions specified # are assigned in _update_state_input_ports_for_controller. if self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR: - warnings.warn(f"No 'state_features' specified for use with `agent_rep' of {self.name}") + warnings.warn(f"No '{STATE_FEATURES}' specified for use with `agent_rep' of {self.name}") else: # Implement any specified state_features @@ -1655,15 +1670,364 @@ def _instantiate_input_ports(self, context=None): f"{port.name} should receive exactly one projection, " f"but it receives {len(port.path_afferents)} projections.") - def _validate_monitor_for_control(self, nodes): - # Ensure all of the Components being monitored for control are in the agent_rep if it is Composition - if self.agent_rep_type == COMPOSITION: - try: - super()._validate_monitor_for_control(self.agent_rep._get_all_nodes()) - except ControlMechanismError as e: - raise OptimizationControlMechanismError(f"{self.name} has 'outcome_ouput_ports' that receive " - f"Projections from the following Components that do not belong " - f"to its {AGENT_REP} ({self.agent_rep.name}): {e.data}.") + def _get_agent_rep_input_nodes(self, comp=None, comp_as_node:Union[bool,ALL]=False): + """Return all input_nodes of agent_rep, including those for any Composition nested one level down. + If an INPUT Node is a Composition, and include_comp_as_node is: + - False, include the nested Composition's INPUT Nodes, but not the Composition + - True, include the nested Composition but not its INPUT Nodes + - ALL, include the nested Composition AND its INPUT Nodes + """ + from psyneulink.core.compositions.composition import Composition, NodeRole + if not self.agent_rep_type or self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR: + return [None] + comp = comp or self.agent_rep + _input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) + input_nodes = [] + for node in _input_nodes: + if isinstance(node, Composition): + if comp_as_node: + input_nodes.append(node) + if comp_as_node in {False, ALL}: + # FIX: DOESN'T THIS SEARCH RECURSIVELY? -- NEED TO TEST WITH > ONE LEVEL OF NESTING + input_nodes.extend(self._get_agent_rep_input_nodes(node)) + else: + input_nodes.append(node) + return input_nodes + + def _get_nodes_not_in_agent_rep(self, state_feature_specs): + from psyneulink.core.compositions.composition import Composition + agent_rep_nodes = self.agent_rep._get_all_nodes() + return [spec for spec in state_feature_specs + if ((isinstance(spec, (Mechanism, Composition)) + and spec not in agent_rep_nodes) + or (isinstance(spec, Port) + and spec.owner not in agent_rep_nodes))] + + def _validate_input_nodes(self, nodes, enforce=None): + """Check that nodes are INPUT Nodes of agent_rep + Raise exception for non-INPUT Nodes if **enforce** is specified; else warn. + """ + non_input_node_specs = [node for node in nodes + if node not in self._get_agent_rep_input_nodes(comp_as_node=True)] + non_agent_rep_node_specs = [node for node in nodes if node not in self.agent_rep._get_all_nodes()] + + # Deal with Nodes that are in agent_rep but not INPUT Nodes + if non_input_node_specs: + items = ', '.join([n._name for n in non_input_node_specs]) + if len(non_input_node_specs) == 1: + items_str = f"contains an item ({items}) that is not an INPUT Node" + else: + items_str = f"contains items ({items}) that are not INPUT Nodes" + message = f"The '{STATE_FEATURES}' specified for '{self.name}' {items_str} " \ + f"of its {AGENT_REP} ('{self.agent_rep.name}'); only INPUT Nodes can be in a set " \ + f"or used as keys in a dict used to specify '{STATE_FEATURES}'." + if enforce: + raise OptimizationControlMechanismError(message) + else: + warnings.warn(message) + + # Deal with Nodes that are not in agent_rep + if non_agent_rep_node_specs: + items = ', '.join([n._name for n in non_agent_rep_node_specs]) + singular = len(non_agent_rep_node_specs) == 1 + if singular: + items_str = f"contains an item ({items}) that is" + else: + items_str = f"contains items ({items}) that are" + message = f"The '{STATE_FEATURES}' specified for '{self.name}' {items_str} not in its {AGENT_REP} " \ + f"('{self.agent_rep.name}'). Executing '{self.agent_rep.name}' before " \ + f"{'they' if singular else 'it'} are added will generate an error ." + if enforce: + raise OptimizationControlMechanismError(message) + else: + warnings.warn(message) + + def _parse_state_feature_specs(self, state_feature_specs, feature_functions, context=None): + """Parse entries of state_features specifications into InputPort spec dictionaries. + + Called from _instantiate_input_ports() + + state_features specify sources of values assigned to state_feature_values, and passed to agent_rep.evaluate() + as the inputs to its INPUT Nodes. + Each is used to create Projection(s) from specified source (may be direct or indirect from a nested Composition) + If the number of state_features specified is less than the number of agent_rep INPUT Nodes, + do not construct a state_input_port (as a result, no input is provided to the corresponding INPUT Node + when agent_rep.evaluate() executes, and the value of that NODE from its last execution is used. + If shadowing is specified, set INTERNAL_ONLY to True in entry of params dict for state_input_port's InputPort + specification dictionary (so that inputs to Composition are not required if the specified state_feature + is for an INPUT Node). + If an INPUT Node is specified that is not (yet) in agent_rep, and/or a source is specified that is not yet + in the self.composition, warn and defer creating a state_input_port; final check is made (and error + generated for unresolved specifications at run time. + + Handle four formats: + - list: list of sources; must be listed in same order as INPUT Nodes of agent_rep to which they correspond; + - dict: keys are INPUT Nodes of agent_rep and values are corresponding sources; + - set: INPUT Nodes for which shadowing is specified + - SHADOW_INPUTS dict: single entry with key='SHADOW_INPUTS" and values=list of sources (see above). + + Assign functions specified in **state_feature_functions** to InputPorts for all state_features + + Return list of InputPort specification dictionaries for state_input_ports + """ + + from psyneulink.core.compositions.composition import Composition, NodeRole + # Agent rep's input Nodes and their names + agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) + + # VALIDATION AND WARNINGS ----------------------------------------------------------------------------------- + + assert state_feature_specs == self.state_feature_specs, \ + f"PROGRAM ERROR: self.state_feature_specs for {self.name} not passed to _parse_state_feature_specs()." + # MODIFIED 1/26/22 NEW: FIX: MODIFY ONCE self.state_feature_specs is a Parameter(structural=True) + state_feature_specs = state_feature_specs.copy() + # MODIFIED 1/26/22 END + + # Only list spec allowed if agent_rep is a CompositionFunctionApproximator + if self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR and not isinstance(state_feature_specs, list): + agent_rep_name = f" ({self.agent_rep.name})" if not isinstance(self.agent_rep, type) else '' + raise OptimizationControlMechanismError( + f"The {AGENT_REP} specified for {self.name}{agent_rep_name} is a {COMPOSITION_FUNCTION_APPROXIMATOR}, " + f"so its '{STATE_FEATURES}' argument must be a list, not a {type(state_feature_specs).__name__} " + f"({state_feature_specs}).") + + # agent_rep has not yet been (fully) constructed + if not agent_rep_input_nodes and self.agent_rep_type is COMPOSITION: + if (isinstance(state_feature_specs, set) + or isinstance(state_feature_specs, dict) and SHADOW_INPUTS not in state_feature_specs): + # Dict and set specs reference Nodes of agent_rep, and so must that must be constructed first + raise OptimizationControlMechanismError( + f"The '{STATE_FEATURES}' arg for {self.name} has been assigned a dict or set specification " + f"before any Nodes have been assigned to its {AGENT_REP} ('{self.agent_rep.name}'). Either" + f"those should be assigned before construction of {self.name}, or a list specification " + f"should be used (though that is not advised).") + else: + # List and SHADOW_INPUTS specs are dangerous before agent_rep has been fully constructed + warnings.warn(f"The {STATE_FEATURES}' arg for {self.name} has been specified before any Nodes have " + f"been assigned to its {AGENT_REP} ('{self.agent_rep.name}'). Their order must be the " + f"same as the order of the corresponding INPUT Nodes for '{self.agent_rep.name}' once " + f"they are added, or unexpected results may occur. It is safer to assign all Nodes to " + f"the {AGENT_REP} of a controller before specifying its '{STATE_FEATURES}'.") + else: + # # FIX: 1/16/22 - MAY BE A PROBLEM IF SET OR DICT HAS ENTRIES FOR INPUT NODES OF NESTED COMP THAT IS AN INPUT NODE + # FIX: 1/18/22 - ADD TEST FOR THIS WARNING TO test_ocm_state_feature_specs_and_warnings_and_errors: too_many_inputs + if len(state_feature_specs) < len(agent_rep_input_nodes): + warnings.warn(f"There are fewer '{STATE_FEATURES}' specified for '{self.name}' than the number of " + f"INPUT Nodes of its {AGENT_REP} ('{self.agent_rep.name}'); the remaining inputs will be " + f"assigned default values when '{self.agent_rep.name}`s 'evaluate' method is executed. " + f"If this is not the desired configuration, use its get_inputs_format() method to see " + f"the format for all of its inputs.") + + # HELPER METHODS ------------------------------------------------------------------------------------------ + + def expand_nested_input_comp_to_input_nodes(comp): + input_nodes = [] + for node in comp.get_nodes_by_role(NodeRole.INPUT): + if isinstance(node, Composition): + input_nodes.extend(expand_nested_input_comp_to_input_nodes(node)) + else: + input_nodes.append(node) + return input_nodes + + def get_nodes_in_agent_rep_order(): + # FIX: 1/26/22 - MAKES ASSUMPTION THAT NODES NOT YET IN agent_rep WILL BE ADDED IN ORDER LISTED + # Re-order nodes according to their order in agent_rep.nodes, and append any not (yet) in agent_rep at end + # (assumes that ones not listed in order will be added to agent_rep later in the order listed) + node_specs = list(state_feature_specs) + nodes = [] + for node in agent_rep_input_nodes: + if node in node_specs: + nodes.append(node_specs.pop(node_specs.index(node))) + nodes.extend(node_specs) + return nodes + + def get_inputs_for_nested_comp(comp): + # FIX: 1/18/22 - NEEDS TO BE MODIFIED TO RETURN TUPLE IF > INPUT NODE, ONCE THAT CAN BE HANDLED BY LIST SPEC + return comp.get_nodes_by_role(NodeRole.INPUT) + + # List of INPUT Nodes ordered for use elswewhere (e.g., self.state_features) + self._specified_input_nodes_in_order = [] + source_specs_for_input_nodes = [] + + def instantiate_list_spec(state_feature_specs, spec_str="list"): + if self.agent_rep_type == COMPOSITION: + if len(state_feature_specs) > len(agent_rep_input_nodes): + nodes_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" + for spec in self._get_nodes_not_in_agent_rep(state_feature_specs)] + if nodes_not_in_agent_rep: + node_str = ", ".join(nodes_not_in_agent_rep) + warnings.warn( + f"The number of '{STATE_FEATURES}' specified for {self.name} " + f"({len(self.state_feature_specs)}) is more than the number of INPUT Nodes " + f"({len(agent_rep_input_nodes)}) of the Composition assigned as its {AGENT_REP} " + f"('{self.agent_rep.name}'), which includes the following that " + f"are not in '{self.agent_rep.name}': {node_str}. Executing {self.name} before the " + f"additional Node(s) are added as INPUT Nodes will generate an error.") + else: + warnings.warn( + f"The number of '{STATE_FEATURES}' specified for {self.name} " + f"({len(self.state_feature_specs)}) is more than the number of INPUT Nodes " + f"({len(agent_rep_input_nodes)}) of the Composition assigned as its {AGENT_REP} " + f"('{self.agent_rep.name}'). Executing {self.name} before the " + f"additional Nodes are added as INPUT Nodes will generate an error.") + + # Nested Compositions not allowed to be specified in a list spec + nested_comps = [node for node in state_feature_specs if isinstance(node, Composition)] + if nested_comps: + comp_names = ", ".join([f"'{n.name}'" for n in nested_comps]) + raise OptimizationControlMechanismError( + f"The '{STATE_FEATURES}' argument for '{self.name}' includes one or more Compositions " + f"({comp_names}) in the {spec_str} specified for its '{STATE_FEATURES}' argument; these must be " + f"replaced by direct references to the Mechanisms (or their InputPorts) within them to be " + f"shadowed.") + # Get INPUT Nodes for items in list (assuming items are in order of INPUT Nodes of agent_rep) + nodes = [] + specs = [] + spec_names = [] + for i, spec in enumerate(state_feature_specs): + if spec is None: + continue + # Assign spec + specs.append(spec) + # Only process specs for which there are already INPUT Nodes in agent_rep + # (others may be added to Composition later) + if i < len(agent_rep_input_nodes): + # Assign node + node = agent_rep_input_nodes[i] + nodes.append(node) + # Assign input_port name + if is_numeric(spec): + spec_names.append(f"{node.name} {DEFAULT_VARIABLE.upper()}") + else: + if hasattr(spec, 'full_name'): + spec_names.append(spec.full_name) + else: + spec_names.append(spec.name) + + return nodes or None, specs, spec_names or [] + + # PARSE SPECS ------------------------------------------------------------------------------------------ + # Generate parallel lists of INPUT Nodes and corresponding feature specs (for sources of inputs) + + # LIST spec + # Treat as source specs: + # - construct a regular dict using INPUT Nodes as keys and specs as values + if isinstance(state_feature_specs, list): + nodes, specs, names = instantiate_list_spec(state_feature_specs) + state_feature_specs = specs + self._specified_input_nodes_in_order = nodes + input_port_names = names + + # DICT spec + elif isinstance(state_feature_specs, dict): + # SHADOW_INPUTS dict spec + if SHADOW_INPUTS in state_feature_specs: + if isinstance(state_feature_specs[SHADOW_INPUTS], set): + # Catch here to provide context-relevant error message + raise OptimizationControlMechanismError( + f"The '{STATE_FEATURES}' argument for '{self.name}' uses a set in a '{SHADOW_INPUTS.upper()}' " + f"dict; this must be a single item or list of specifications in the order of the INPUT Nodes" + f"of its '{AGENT_REP}' ({self.agent_rep.name}) to which they correspond." ) + nodes, specs, names = instantiate_list_spec(state_feature_specs[SHADOW_INPUTS], + f"{SHADOW_INPUTS.upper()} dict") + state_feature_specs[SHADOW_INPUTS] = specs + self._specified_input_nodes_in_order = nodes + input_port_names = names + + # User {node:spec} dict spec + else: + state_feature_specs = {k:v for k,v in state_feature_specs.items() if v is not None} + # Get INPUT nodes in order listed in agent_rep.nodes + self._specified_input_nodes_in_order = get_nodes_in_agent_rep_order() + self._validate_input_nodes(state_feature_specs.keys()) + # Get specs in the same order: + specs = [state_feature_specs[node] for node in self._specified_input_nodes_in_order] + # Get parsed specs and names (don't care about nodes since those are specified by keys + _ , state_feature_specs, input_port_names = instantiate_list_spec(specs) + + # SET spec + # Treat as specification of INPUT Nodes to be shadowed: + # - construct an InputPort dict with SHADOW_INPUTS as its key, and specs in a list as its value + elif isinstance(state_feature_specs, set): + # All nodes must be INPUT nodes of agent_rep, that are to be shadowed, + # Order the set and place in list + self._specified_input_nodes_in_order = get_nodes_in_agent_rep_order() + self._validate_input_nodes(state_feature_specs) + # Replace any nested Comps that are INPUT Nodes of agent_comp with their INPUT Nodes so they are shadowed + all_nested_input_nodes = [] + for node in self._specified_input_nodes_in_order: + if isinstance(node, Composition): + all_nested_input_nodes.extend(get_inputs_for_nested_comp(node)) + else: + all_nested_input_nodes.append(node) + # so reformat as SHADOW_INPUTS dict handled by _parse_shadow_inputs() below + source_specs_for_input_nodes = all_nested_input_nodes + state_feature_specs = {SHADOW_INPUTS:source_specs_for_input_nodes} + + # CONSTRUCT InputPort SPECS ----------------------------------------------------------------------------- + + _state_input_ports = _parse_shadow_inputs(self, state_feature_specs) + + parsed_features = [] + + for i, spec in enumerate(_state_input_ports): + # If spec is numeric, assign as default value and InputPort function that simply returns that value + if is_numeric(spec): + spec_val = copy.copy(spec) + spec = {VALUE: spec_val, + PARAMS: {DEFAULT_INPUT: DEFAULT_VARIABLE} + } + # If optimization uses Composition, assume that shadowing a Mechanism means shadowing its primary InputPort + if isinstance(spec, Mechanism): + if self.agent_rep_type == COMPOSITION: + # FIX: 11/29/21: MOVE THIS TO _parse_shadow_inputs + # (ADD ARG TO THAT FOR DOING SO, OR RESTRICTING TO INPUTPORTS IN GENERAL) + if len(spec.input_ports)!=1: + raise OptimizationControlMechanismError(f"A Mechanism ({spec.name}) is specified in the " + f"'{STATE_FEATURES}' arg for {self.name} that has " + f"more than one InputPort; a specific one or subset " + f"of them must be specified.") + spec = spec.input_port + else: + spec = spec.output_port + parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) + + if not parsed_spec[NAME]: + if i < len(input_port_names): + # Use keys from input dict as names of state_input_ports + # (needed by comp._build_predicted_inputs_dict to identify INPUT nodes) + parsed_spec[NAME] = input_port_names[i] + else: + parsed_spec[NAME] = spec.full_name if isinstance(spec, Port) else spec.name + + if parsed_spec[PARAMS] and SHADOW_INPUTS in parsed_spec[PARAMS]: + # Composition._update_shadow_projections will take care of PROJECTIONS specification + parsed_spec[PARAMS].update({INTERNAL_ONLY:True, + PROJECTIONS:None}) + + # GET FEATURE FUNCTIONS ----------------------------------------------------------------------------- + + if feature_functions: + if isinstance(feature_functions, dict) and spec in feature_functions: + feat_fct = feature_functions.pop(spec) + else: + feat_fct = feature_functions + parsed_spec.update({FUNCTION: self._parse_state_feature_function(feat_fct)}) + parsed_spec = [parsed_spec] # so that extend works below + + parsed_features.extend(parsed_spec) + + # # # MODIFIED 1/24/22 OLD: + # # FIX - 1/25/22: REINSTATE ONCE self.state_feature_specs IS A PARAMETER WITH structural=True + # self.state_feature_specs = state_feature_specs + # MODIFIED 1/24/22 END + return parsed_features + + def _parse_state_feature_function(self, feature_function): + if isinstance(feature_function, Function): + return copy.deepcopy(feature_function) + else: + return feature_function def _update_state_input_ports_for_controller(self, context=None): """Check and update state_input_ports for model-based optimization (agent_rep==Composition) @@ -1685,10 +2049,12 @@ def _update_state_input_ports_for_controller(self, context=None): - assign state_feature_functions to relevant state_input_ports (same function for all if no state_features are specified or only one state_function is specified; otherwise, use dict for specifications). + + Return True if successful, None if not performed. """ # Don't instantiate unless being called by Composition.run() - # This avoids error messages if called prematurely (i.e., before run is complete) + # This avoids error messages if called prematurely (i.e., before construction of Composition is complete) if context.flags & ContextFlags.PROCESSING: return @@ -1700,14 +2066,20 @@ def _update_state_input_ports_for_controller(self, context=None): return if self.state_feature_specs: - self._validate_state_features() + # Restrict validation and any further instantiation of state_input_ports + # until run time, when the Composition is expected to be fully constructed + if context._execution_phase == ContextFlags.PREPARING: + self._validate_state_features() return - else: + elif not self.state_input_ports: # agent_rep is Composition, but no state_features have been specified, # so assign a state_input_port to shadow every InputPort of every INPUT node of agent_rep shadow_input_ports = [] - for node in self._get_agent_rep_input_nodes(): + + # Get list of nodes with any nested Comps that are INPUT Nodes replaced with their respective INPUT Nodes + # (as those are what need to be shadowed) + for node in self._get_agent_rep_input_nodes(comp_as_node=False): for input_port in node.input_ports: if input_port.internal_only: continue @@ -1715,6 +2087,10 @@ def _update_state_input_ports_for_controller(self, context=None): # input_port = input_port. shadow_input_ports.append(input_port) + # Get list of nodes with any nested Comps that are INPUT Nodes listed as the node + # (this is what is used in the state_features dict and state_features property) + self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) + local_context = Context(source=ContextFlags.METHOD) state_input_ports_to_add = [] # for input_port in input_ports_not_specified: @@ -1737,46 +2113,64 @@ def _update_state_input_ports_for_controller(self, context=None): context=local_context) self.state_input_ports.extend(state_input_ports_to_add) - # input_values = self.state_input_ports.values - # input_nodes = self.composition._build_predicted_inputs_dict(input_values) - # sources = [self.composition._get_source(n.path_afferents[0]) for n in self.state_input_ports] - # - # # MODIFIED 1/15 OLD: - # # FIX: NEEDS TO USE _get_source() for INPUT nodes - # # self.state_features = self.composition._parse_input_dict(input_nodes) - # self.state_features = {k:v for k,v in zip(input_nodes, sources)} - # # # MODIFIED 1/15 NEW: - # # self.parameters.state_features.set(self.composition._parse_input_dict(input_nodes), - # # override=True) - # # MODIFIED 1/15 END + return True def _validate_state_features(self): + """Validate that state_features are legal and consistent with agent_rep. + + Called by _update_state_input_ports_for_controller, + - after new Nodes have been added to Composition + - and/or in run() as final check before execution. + + Ensure that: + - if state_feature_specs are speified as a user dict, keys are valid INPUT Nodes of agent_rep; + - all InputPorts shadowed by specified state_input_ports are in agent_rep or one of its nested Compositions; + - any Projections received from output_ports are from Nodes in agent_rep or its nested Compositions; + - all InputPorts shadowed by state_input_ports reference INPUT Nodes of agent_rep or of a nested Composition; + - state_features are compatible with input format for agent_rep Composition + """ + from psyneulink.core.compositions.composition import \ Composition, CompositionInterfaceMechanism, CompositionError, RunError, NodeRole - # Validate state_features, and instantiate any that are not shadowing nodes - # Shadowing nodes are instantiated in Composition._update_shadow_projections() comp = self.agent_rep - if isinstance(self.state_feature_specs, list): + if isinstance(self.state_feature_specs, dict) and SHADOW_INPUTS in self.state_feature_specs: + state_feature_specs = self.state_feature_specs[SHADOW_INPUTS] + else: + state_feature_specs = self.state_feature_specs + + if isinstance(state_feature_specs, list): # Convert list to dict, assuming list is in order of INPUT Nodes, # and assigning the corresponding INPUT Nodes as keys for use in comp._build_predicted_inputs_dict() input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) - if len(self.state_feature_specs) > len(input_nodes): + if len(state_feature_specs) > len(input_nodes): + nodes_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" + for spec in self._get_nodes_not_in_agent_rep(state_feature_specs)] + missing_nodes_str = (f", that includes the following: {', '.join(nodes_not_in_agent_rep)} " + f"missing from {self.agent_rep.name}" + if nodes_not_in_agent_rep else '') raise OptimizationControlMechanismError( - f"The number of 'state_features' specified for {self.name} ({len(self.state_feature_specs)}) " + f"The number of '{STATE_FEATURES}' specified for {self.name} ({len(state_feature_specs)}) " f"is more than the number of INPUT Nodes ({len(input_nodes)}) of the Composition assigned " - f"as its {AGENT_REP} ('{self.agent_rep.name}').") + f"as its {AGENT_REP} ('{self.agent_rep.name}'){missing_nodes_str}.") input_dict = {} - for i, spec in enumerate(self.state_feature_specs): + for i, spec in enumerate(state_feature_specs): input_dict[input_nodes[i]] = spec - state_features = self.state_feature_specs - elif isinstance(self.state_feature_specs, dict): + state_features = state_feature_specs + elif isinstance(state_feature_specs, dict): + # If user dict is specified, check that keys are legal INPUT nodes: + self._validate_input_nodes(state_feature_specs.keys(), enforce=True) # If dict is specified, get values for checks below - state_features = list(self.state_feature_specs.values()) + state_features = list(state_feature_specs.values()) + elif isinstance(state_feature_specs, set): + # If user dict is specified, check that keys are legal INPUT nodes: + self._validate_input_nodes(state_feature_specs, enforce=True) + # If dict is specified, get values for checks below + state_features = list(state_feature_specs) # Include agent rep in error messages if it is not the same as self.composition - self_has_state_features_str = f"'{self.name}' has 'state_features' specified " + self_has_state_features_str = f"'{self.name}' has '{STATE_FEATURES}' specified " agent_rep_str = ('' if self.agent_rep == self.composition else f"both its `{AGENT_REP}` ('{self.agent_rep.name}') as well as ") not_in_comps_str = f"that are missing from {agent_rep_str}'{self.composition.name}' and any " \ @@ -1819,23 +2213,28 @@ def _validate_state_features(self): raise OptimizationControlMechanismError( self_has_state_features_str + f"({[d.name for d in invalid_state_features]}) " + not_in_comps_str) + # # FOR DEBUGGING: (TO SEE CODING ERRORS DIRECTLY) + # inputs = self.agent_rep._build_predicted_inputs_dict(None, self) + # inputs_dict, num_inputs = self.agent_rep._parse_input_dict(inputs) + # if len(self.state_input_ports) < len(inputs_dict): + # warnings.warn(f"The '{STATE_FEATURES}' specified for '{self.name}' are legal, but there are fewer " + # f"than the number of input_nodes for its {AGENT_REP} ('{self.agent_rep.name}'); " + # f"the remaining inputs will be assigned default values. Use the {AGENT_REP}'s " + # f"get_inputs_format() method to see the format for its inputs.") + # Ensure state_features are compatible with input format for agent_rep Composition try: # FIX: 1/10/22 - ?USE self.agent_rep.external_input_values FOR CHECK? + # Call these to check for errors in construcing inputs dict inputs = self.agent_rep._build_predicted_inputs_dict(None, self) - inputs_dict, num_inputs = self.agent_rep._parse_input_dict(inputs) - if len(self.state_input_ports) < len(inputs_dict): - warnings.warn(f"The 'state_features' specified for '{self.name}' are legal, but there are fewer " - f"than the number of input_nodes for its {AGENT_REP} ('{self.agent_rep.name}'); " - f"the remaining inputs will be assigned default values. Use the {AGENT_REP}'s " - f"get_inputs_format() method to see the format for its inputs.") + self.agent_rep._parse_input_dict(inputs) except RunError as error: raise OptimizationControlMechanismError( - f"The 'state_features' argument has been specified for '{self.name}' that is using a " + f"The '{STATE_FEATURES}' argument has been specified for '{self.name}' that is using a " f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but " f"they are not compatible with the inputs required by its 'agent_rep': '{error.error_value}' " f"Use the get_inputs_format() method of '{self.agent_rep.name}' to see the required format, or " - f"remove the specification of 'state_features' from the constructor for {self.name} " + f"remove the specification of '{STATE_FEATURES}' from the constructor for {self.name} " f"to have them automatically assigned.") except KeyError as error: # This occurs if a Node is illegal for a reason other than above, pass # and will issue the corresponding error message. @@ -1843,13 +2242,23 @@ def _validate_state_features(self): specs = [f.full_name if hasattr(f, 'full_name') else (f.name if isinstance(f, Component) else f) for f in state_features] raise OptimizationControlMechanismError( - f"The 'state_features' argument has been specified for '{self.name}' that is using a " + f"The '{STATE_FEATURES}' argument has been specified for '{self.name}' that is using a " f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but the " - f"'state_features' ({specs}) specified are not compatible with the inputs required by 'agent_rep' " + f"'{STATE_FEATURES}' ({specs}) specified are not compatible with the inputs required by 'agent_rep' " f"when it is executed. Use its get_inputs_format() method to see the required format, " - f"or remove the specification of 'state_features' from the constructor for {self.name} " + f"or remove the specification of '{STATE_FEATURES}' from the constructor for {self.name} " f"to have them automatically assigned.") + def _validate_monitor_for_control(self, nodes): + # Ensure all of the Components being monitored for control are in the agent_rep if it is Composition + if self.agent_rep_type == COMPOSITION: + try: + super()._validate_monitor_for_control(self.agent_rep._get_all_nodes()) + except ControlMechanismError as e: + raise OptimizationControlMechanismError(f"{self.name} has 'outcome_ouput_ports' that receive " + f"Projections from the following Components that do not belong " + f"to its {AGENT_REP} ({self.agent_rep.name}): {e.data}.") + def _instantiate_output_ports(self, context=None): """Assign CostFunctions.DEFAULTS as default for cost_option of ControlSignals. """ @@ -2510,105 +2919,14 @@ def _gen_llvm_output_port_parse_variable(self, ctx, builder, params, context, va @property def agent_rep_type(self): from psyneulink.core.compositions.compositionfunctionapproximator import CompositionFunctionApproximator - if isinstance(self.agent_rep, CompositionFunctionApproximator): + if (isinstance(self.agent_rep, CompositionFunctionApproximator) + or self.agent_rep.componentCategory is COMPOSITION_FUNCTION_APPROXIMATOR): return COMPOSITION_FUNCTION_APPROXIMATOR elif self.agent_rep.componentCategory=='Composition': return COMPOSITION else: return None - def _get_agent_rep_input_nodes(self, comp=None): - """Return all input_nodes of agent_rep, including those for any Composition nested one level down. - Note: more deeply nested Compositions will either be served by their containing one(s) or own controllers - """ - from psyneulink.core.compositions.composition import Composition, NodeRole - if not self.agent_rep_type: - return [None] - comp = comp or self.agent_rep - _input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) - input_nodes = [] - for node in _input_nodes: - if isinstance(node, Composition): - input_nodes.extend(self._get_agent_rep_input_nodes(node)) - else: - input_nodes.append(node) - return input_nodes - - def _parse_state_feature_function(self, feature_function): - if isinstance(feature_function, Function): - return copy.deepcopy(feature_function) - else: - return feature_function - - @tc.typecheck - def _parse_state_feature_specs(self, state_features, feature_functions, context=None): - """Parse entries of state_features into InputPort spec dictionaries - For entries specifying shadowing, set INTERNAL_ONLY entry of params dict for InputPort spec dictionary to True - (so that inputs to Composition are not required if the specified state feature is on an INPUT Mechanism) - Assign functions specified in **state_feature_functions** to InputPorts for all state_features - Return list of InputPort specification dictionaries for state_input_ports - """ - input_node_names = [n.name if n else None for n in self._get_agent_rep_input_nodes()] - input_port_names = None - if isinstance(state_features, dict): - if SHADOW_INPUTS in self.state_feature_specs: - pass # handled below - else: - input_port_names = [k.name for k in list(state_features.keys())] - state_features = list(state_features.values()) - - _state_input_ports = _parse_shadow_inputs(self, state_features) - - parsed_features = [] - - for i, spec in enumerate(_state_input_ports): - # If spec is numeric, assign as default value and InputPort function that simply returns that value - if is_numeric(spec): - spec_val = copy.copy(spec) - spec = {VALUE: spec_val, - PARAMS: {DEFAULT_INPUT: DEFAULT_VARIABLE} - } - # If optimization uses Composition, assume that shadowing a Mechanism means shadowing its primary InputPort - if isinstance(spec, Mechanism): - if self.agent_rep_type == COMPOSITION: - # FIX: 11/29/21: MOVE THIS TO _parse_shadow_inputs - # (ADD ARG TO THAT FOR DOING SO, OR RESTRICTING TO INPUTPORTS IN GENERAL) - if len(spec.input_ports)!=1: - raise OptimizationControlMechanismError(f"A Mechanism ({spec.name}) is specified in the " - f"'{STATE_FEATURES}' arg for {self.name} that has " - f"more than one InputPort; a specific one or subset " - f"of them must be specified.") - spec = spec.input_port - else: - spec = spec.output_port - parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) - - if input_port_names: - # Use keys from input dict as names of state_input_ports - # (needed by comp._build_predicted_inputs_dict to identify INPUT nodes) - parsed_spec[NAME] = input_port_names[i] - elif not parsed_spec[NAME]: - if isinstance(spec, Port): - parsed_spec[NAME] = spec.full_name - else: - # Assumes specs specified in order of agent_rep's INPUT Nodes - parsed_spec[NAME] = input_node_names[i] - if parsed_spec[PARAMS] and SHADOW_INPUTS in parsed_spec[PARAMS]: - # Composition._update_shadow_projections will take care of PROJECTIONS specification - parsed_spec[PARAMS].update({INTERNAL_ONLY:True, - PROJECTIONS:None}) - if feature_functions: - if isinstance(feature_functions, dict) and spec in feature_functions: - feat_fct = feature_functions.pop(spec) - else: - feat_fct = feature_functions - parsed_spec.update({FUNCTION: self._parse_state_feature_function(feat_fct)}) - parsed_spec = [parsed_spec] # so that extend works below - - parsed_features.extend(parsed_spec) - - return parsed_features - @property def num_state_input_ports(self): try: @@ -2618,10 +2936,34 @@ def num_state_input_ports(self): @property def state_features(self): - from psyneulink.core.compositions.composition import NodeRole - input_nodes = self.composition.get_nodes_by_role(NodeRole.INPUT) + """Return dict with {INPUT Node: source} entries for specifications in **state_features** arg of constructor.""" + + num_instantiated_state_features = len([p for p in self.state_input_ports + if p.path_afferents or p.default_input]) + input_nodes = None + state_features = None + if isinstance(self.state_feature_specs, dict): + if SHADOW_INPUTS in self.state_feature_specs: + # FIX: 1/26/22 - NEED TO DEAL WITH None + # input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) + state_features = self.state_feature_specs[SHADOW_INPUTS] + input_nodes = [node for node, spec in zip(self._get_agent_rep_input_nodes(comp_as_node=True), + state_features) if spec is not None] + else: + # FIX: NOT SURE THIS IS SAFE; ASSUMES ITEMS ARE INSTANTIATED IN ORDER LISTED IN Composition.nodes + input_nodes = self._specified_input_nodes_in_order[:num_instantiated_state_features] + else: + # List spec + state_features = self.state_feature_specs + if not self.state_feature_specs: + # Automatic assignment + input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) + elif not input_nodes: + # List spec + input_nodes = [node for node, spec in zip(self._get_agent_rep_input_nodes(comp_as_node=True), + state_features) if spec is not None] sources = [source_tuple[0] if source_tuple[0] != DEFAULT_VARIABLE else value - for source_tuple,value in list(self.state_dict.items())[:len(self.state_input_ports)]] + for source_tuple,value in list(self.state_dict.items())[:num_instantiated_state_features]] return {k:v for k,v in zip(input_nodes, sources)} @property @@ -2633,7 +2975,10 @@ def state(self): @property def state_dict(self): - """Return dict with (node, port, Composition, index) tuples as keys and corresponding state[index] as values. + """Return dict with (Port, Node, Composition, index) tuples as keys and corresponding state[index] as values. + Initial entries are for sources of the state_feature_values (i.e., distal afferents for state_input_ports) + and subsequent entries are for parameters modulated by the OptimizationControlMechanism's ControlSignals + (i.e., distal destinations of its ControlProjections). Note: the index is required, since a state_input_port may have more than one afferent Projection (that is, a state_feature_value may be determined by Projections from more than one Node), and a ControlSignal may have more than one ControlProjection (that is, a given element of the @@ -2648,27 +2993,25 @@ def state_dict(self): # Get sources for state_feature_values of state: for state_index, port in enumerate(self.state_input_ports): - get_info_method = self.composition._get_source - # MODIFIED 1/8/22: ONLY ONE PROJECTION PER STATE FEATURE - if port.shadow_inputs: - port = port.shadow_inputs - if port.owner in self.composition.nodes: - composition = self.composition - else: - composition = port.path_afferents[0].sender.owner.composition - get_info_method = composition._get_destination if not port.path_afferents: if port.default_input is DEFAULT_VARIABLE: source_port = DEFAULT_VARIABLE node = None comp = None else: - assert port.path_afferents, f"PROGRAM ERROR: state_input_port {state_index} ('{port.name}')" \ - f"for {self.name} does not have any Projections to it" + continue else: + get_info_method = self.composition._get_source + # MODIFIED 1/8/22: ONLY ONE PROJECTION PER STATE FEATURE + if port.shadow_inputs: + port = port.shadow_inputs + if port.owner in self.composition.nodes: + composition = self.composition + else: + composition = port.path_afferents[0].sender.owner.composition + get_info_method = composition._get_destination source_port, node, comp = get_info_method(port.path_afferents[0]) state_dict.update({(source_port, node, comp, state_index):self.state[state_index]}) - state_index += 1 # Get recipients of control_allocations values of state: for ctl_index, control_signal in enumerate(self.control_signals): @@ -2702,18 +3045,19 @@ def _initialize_composition_function_approximator(self, context): control_signals = self.control_signals, context=context) - # FIX: THE FOLLOWING SHOULD BE MERGED WITH HANDLING OF PredictionMechanisms FOR ORIG MODEL-BASED APPROACH; - # FIX: SHOULD BE GENERALIZED AS SOMETHING LIKE update_feature_values - @tc.typecheck - @handle_external_context() - def add_state_features(self, features, context=None): - """Add InputPorts and Projections to OptimizationControlMechanism for state_features used to - predict `net_outcome ` - - **state_features** argument can use any of the forms of specification allowed for InputPort(s) - """ - - if features: - features = self._parse_state_feature_specs(features=features, - context=context) - self.add_ports(InputPort, features) + # # FIX: NEEDS TO BE UPDATED / REFACTORED TO WORK WITH _parse_state_feature_specs + # # FIX: THE FOLLOWING SHOULD BE MERGED WITH HANDLING OF PredictionMechanisms FOR ORIG MODEL-BASED APPROACH; + # # FIX: SHOULD BE GENERALIZED AS SOMETHING LIKE update_feature_values + # @tc.typecheck + # @handle_external_context() + # def add_state_features(self, features, context=None): + # """Add InputPorts and Projections to OptimizationControlMechanism for state_features used to + # predict `net_outcome ` + # + # **state_features** argument can use any of the forms of specification allowed for InputPort(s) + # """ + # + # if features: + # features = self._parse_state_feature_specs(features=features, + # context=context) + # self.add_ports(InputPort, features) diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 1479dfb9b25..6abf114a679 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -1282,11 +1282,7 @@ def _parse_self_port_type_spec(self, owner, input_port, context=None): f"with non-InputPort specification ({input_port}).") sender_output_ports = [p.sender for p in input_port.path_afferents] - # MODIFIED 1/12/22 OLD: - port_spec = {NAME: SHADOW_INPUT_NAME + input_port.owner.name, - # # MODIFIED 1/12/22 NEW: - # port_spec = {NAME: SHADOW_INPUT_NAME + input_port.full_name, - # MODIFIED 1/12/22 END + port_spec = {NAME: SHADOW_INPUT_NAME + input_port.full_name, VARIABLE: np.zeros_like(input_port.variable), PORT_TYPE: InputPort, PROJECTIONS: sender_output_ports, diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index c2a66e9ccb3..71952dca1a0 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -4438,19 +4438,16 @@ def _complete_init_of_partially_initialized_nodes(self, context=None): completed_nodes.append(node) self._partially_added_nodes = list(set(self._partially_added_nodes) - set(completed_nodes)) - # Don't instantiate unless flagged for updating (if nodes have been added to the graph); - # this avoids unnecessary calls on repeated calls to run(). - if (self.controller - and self.needs_update_controller - and context.flags & (ContextFlags.COMPOSITION | ContextFlags.COMMAND_LINE)): - if hasattr(self.controller, 'state_input_ports'): - self.controller._update_state_input_ports_for_controller(context=context) - # self._instantiate_controller_shadow_projections(context=context) - self.controller._validate_monitor_for_control(self._get_all_nodes()) - self._instantiate_control_projections(context=context) - # FIX: 11/15/21 - CAN'T SET TO FALSE HERE, AS THIS IS CALLED BY _analyze_graph() FROM add_node() - # BEFORE PROJECTIONS TO THE NODE HAS BEEN ADDED (AFTER CALL TO add_node()) - self.needs_update_controller = False + if self.controller: + # Avoid unnecessary updating on repeated calls to run() + if self.needs_update_controller and hasattr(self.controller, 'state_input_ports'): + self.needs_update_controller = \ + not self.controller._update_state_input_ports_for_controller(context=context) + + # Make sure all is in order at run time + if context.flags & ContextFlags.PREPARING: + self.controller._validate_monitor_for_control(self._get_all_nodes()) + self._instantiate_control_projections(context=context) def _determine_node_roles(self, context=None): """Assign NodeRoles to Nodes in Composition @@ -7720,6 +7717,8 @@ def add_controller(self, controller:ControlMechanism, context=None): invalid_aux_components = self._get_invalid_aux_components(controller) if invalid_aux_components: self._controller_initialization_status = ContextFlags.DEFERRED_INIT + # Need update here so state_features remains up to date + self._analyze_graph(context=context) return # ADD MONITORING COMPONENTS ----------------------------------------------------- @@ -9095,6 +9094,8 @@ def run( """ context.source = ContextFlags.COMPOSITION + execution_phase = context.execution_phase + context.execution_phase = ContextFlags.PREPARING for node in self.nodes: num_execs = node.parameters.num_executions._get(context) @@ -9275,6 +9276,8 @@ def run( else: trial_output = None + context.execution_phase = execution_phase + # EXECUTE TRIALS ------------------------------------------------------------- with Report(self, diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index c78f5a146ce..3892d2526af 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -257,10 +257,16 @@ def test_deferred_init(self, control_spec): np.testing.assert_allclose(comp.results[trial], expected_results_array[trial], atol=1e-08, err_msg='Failed on expected_output[{0}]'.format(trial)) - def test_partial_deferred_init(self): - deferred_node = pnl.ProcessingMechanism(name='deferred') + @pytest.mark.parametrize('state_features_option', [ + 'list', + 'set', + 'dict', + 'shadow_inputs_dict' + ]) + def test_partial_deferred_init(self, state_features_option): initial_node_a = pnl.TransferMechanism(name='ia') initial_node_b = pnl.ProcessingMechanism(name='ib') + deferred_node = pnl.ProcessingMechanism(name='deferred') ocomp = pnl.Composition(name='ocomp', pathways=[initial_node_a, initial_node_b], controller_mode=pnl.BEFORE) @@ -278,12 +284,24 @@ def test_partial_deferred_init(self): allocation_samples=pnl.SampleSpec(start=1.0, stop=5.0, num=5)) + state_features = { + 'list': [initial_node_a.input_port, + deferred_node.input_port], + 'set': {initial_node_a, + deferred_node}, + 'dict': {initial_node_a: initial_node_a.input_port, + deferred_node: deferred_node.input_port}, + 'shadow_inputs_dict': {pnl.SHADOW_INPUTS: [initial_node_a, deferred_node]} + }[state_features_option] ocomp.add_controller( pnl.OptimizationControlMechanism( agent_rep=ocomp, - state_features=[initial_node_a.input_port, - deferred_node.input_port], + # state_features=[initial_node_a.input_port, + # deferred_node.input_port], + # state_features={initial_node_a:initial_node_a.input_port, + # deferred_node:deferred_node.input_port}, + state_features = state_features, name="Controller", objective_mechanism=pnl.ObjectiveMechanism( monitor=initial_node_b.output_port, @@ -296,14 +314,29 @@ def test_partial_deferred_init(self): deferred_node_control_signal ]) ) + assert ocomp.controller.state_features == {initial_node_a: initial_node_a.input_port} + + if state_features_option in {'list', 'shadow_inputs_dict'}: + # expected_text = 'The number of \'state_features\' specified for Controller (2) is more than the ' \ + # 'number of INPUT Nodes (1) of the Composition assigned as its agent_rep (\'ocomp\').' + # + expected_text = 'The number of \'state_features\' specified for Controller (2) is more than the ' \ + 'number of INPUT Nodes (1) of the Composition assigned as its agent_rep (\'ocomp\'), ' \ + 'that includes the following: \'deferred\' missing from ocomp.' + + else: + expected_text = 'The \'state_features\' specified for \'Controller\' contains an item (deferred) ' \ + 'that is not an INPUT Node of its agent_rep (\'ocomp\'); only INPUT Nodes can be ' \ + 'in a set or used as keys in a dict used to specify \'state_features\'.' - expected_text = 'The number of \'state_features\' specified for Controller (2) is more ' \ - 'than the number of INPUT Nodes (1) of the Composition assigned as its agent_rep (\'ocomp\').' with pytest.raises(pnl.OptimizationControlMechanismError) as error_text: ocomp.run({initial_node_a: [1]}) assert expected_text in error_text.value.error_value ocomp.add_linear_processing_pathway([deferred_node, initial_node_b]) + assert ocomp.controller.state_features == {initial_node_a: initial_node_a.input_port, + deferred_node: deferred_node.input_port} + result = ocomp.run({ initial_node_a: [1], deferred_node: [1] @@ -409,7 +442,7 @@ def test_controller_has_no_input(self): comp.add_controller(ctlr) assert expected_warning in repr(warning[0].message.args[0]) - def test_agent_rep_assignement_as_controller_and_replacement(self): + def test_agent_rep_assignment_as_controller_and_replacement(self): mech = pnl.ProcessingMechanism() comp = pnl.Composition(name='comp', pathways=[mech], @@ -627,8 +660,8 @@ class TestControlMechanisms: ), ("state_features_test_not_in_agent_rep", "icomp", "A", "I", True, None, pnl.OptimizationControlMechanismError, - '\'OCM\' has \'state_features\' specified ([\'Shadowed input of A\']) that are missing from both its ' - '`agent_rep` (\'INNER COMP\') as well as \'OUTER COMP\' and any Compositions nested within it.' + '\'OCM\' has \'state_features\' specified ([\'Shadowed input of A[InputPort-0]\']) that are missing ' + 'from both its `agent_rep` (\'INNER COMP\') as well as \'OUTER COMP\' and any Compositions nested within it.' ), ("monitor_for_control_test_not_in_agent_rep", "icomp", "I", "B", True, None, pnl.OptimizationControlMechanismError, @@ -649,6 +682,12 @@ class TestControlMechanisms: "mcomp", "I", None, False, True, pnl.CompositionError, "B found in nested Composition of OUTER COMP (MIDDLE COMP) but without required NodeRole.OUTPUT. " "Try setting 'allow_probes' argument of ObjectiveMechanism for OCM to 'True'." + ), + ("cfa_as_agent_rep_error", + "cfa", "dict", None, False, True, pnl.OptimizationControlMechanismError, + 'The agent_rep specified for OCM is a CompositionFunctionApproximator, so its \'state_features\' argument ' + 'must be a list, not a dict ({(ProcessingMechanism A): (InputPort InputPort-0), ' + '(ProcessingMechanism B): (InputPort InputPort-0)}).' ) ] @pytest.mark.parametrize('id, agent_rep, state_features, monitor_for_control, allow_probes, ' @@ -679,15 +718,18 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c mcomp = pnl.Composition(pathways=[[A,B,C],icomp], name='MIDDLE COMP') ocomp = pnl.Composition(nodes=[mcomp], name='OUTER COMP', allow_probes=allow_probes) + cfa = pnl.RegressionCFA agent_rep = {"mcomp":mcomp, - "icomp":icomp + "icomp":icomp, + "cfa": cfa }[agent_rep] state_features = {"I":I, "Ii A":[I.input_port, A], "A":A, "B":B, + "dict":{A:A.input_port, B:B.input_port} }[state_features] if monitor_for_control: @@ -734,57 +776,95 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ) ocomp.add_controller(ocm) ocomp._analyze_graph() + ocomp.run() assert err.value.error_value == err_msg messages = [ - "The 'state_features' specified for 'OptimizationControlMechanism-0' are legal, " - "but there are fewer than the number of input_nodes for its agent_rep ('OUTER COMP'); " - "the remaining inputs will be assigned default values. " - "Use the agent_rep's get_inputs_format() method to see the format for its inputs.", - - '\'Attempt to shadow the input to a node (IB) in a nested Composition of OUTER COMP ' - 'that is not an INPUT Node of that Composition is not currently supported.\'', - - '"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'Shadowed input of EXT\']) that ' - 'are missing from \'OUTER COMP\' and any Compositions nested within it."', - - '"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'EXT[OutputPort-0]\']) ' - 'that are missing from \'OUTER COMP\' and any Compositions nested within it."', - - "The 'state_features' argument has been specified for 'OptimizationControlMechanism-0' that is using a " - "Composition ('OUTER COMP') as its agent_rep, but they are not compatible with the inputs required by its " - "'agent_rep': 'Input stimulus (0.0) for OB is incompatible with its external_input_values " - "([array([0., 0., 0.])]).' Use the get_inputs_format() method of 'OUTER COMP' to see the required format, " - "or remove the specification of 'state_features' from the constructor for OptimizationControlMechanism-0 to " - "have them automatically assigned.", - - '"The number of \'state_features\' specified for OptimizationControlMechanism-0 (4) is more than the number ' - 'of INPUT Nodes (3) of the Composition assigned as its agent_rep (\'OUTER COMP\')."' + # 0 + f"There are fewer '{pnl.STATE_FEATURES}' specified for 'OptimizationControlMechanism-0' than the number of " + f"INPUT Nodes of its agent_rep ('OUTER COMP'); the remaining inputs will be assigned default values " + f"when 'OUTER COMP`s 'evaluate' method is executed. If this is not the desired configuration, use its " + f"get_inputs_format() method to see the format for all of its inputs.", + + # 1 + f'\'Attempt to shadow the input to a node (IB) in a nested Composition of OUTER COMP ' + f'that is not an INPUT Node of that Composition is not currently supported.\'', + + # 2 + f'"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'Shadowed input of ' + f'EXT[InputPort-0]\']) that are missing from \'OUTER COMP\' and any Compositions nested within it."', + + # 3 + f'"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'EXT[OutputPort-0]\']) ' + f'that are missing from \'OUTER COMP\' and any Compositions nested within it."', + + # 4 + f"The '{pnl.STATE_FEATURES}' argument has been specified for 'OptimizationControlMechanism-0' " + f"that is using a Composition ('OUTER COMP') as its agent_rep, but they are not compatible with " + f"the inputs required by its 'agent_rep': 'Input stimulus (0.0) for OB is incompatible with its " + f"external_input_values ([array([0., 0., 0.])]).' Use the get_inputs_format() method of 'OUTER COMP' " + f"to see the required format, or remove the specification of '{pnl.STATE_FEATURES}' from the constructor " + f"for OptimizationControlMechanism-0 to have them automatically assigned.", + + # 5 + f"The number of '{pnl.STATE_FEATURES}' specified for OptimizationControlMechanism-0 (4) is more than " + f"the number of INPUT Nodes (3) of the Composition assigned as its agent_rep ('OUTER COMP'). " + f"Executing OptimizationControlMechanism-0 before the additional Nodes are added as INPUT Nodes " + f"will generate an error.", + + # 6 + f"The number of 'state_features' specified for OptimizationControlMechanism-0 (4) is more than the number " + f"of INPUT Nodes (3) of the Composition assigned as its agent_rep ('OUTER COMP'), which includes the " + f"following that are not in 'OUTER COMP': 'EXT'. Executing OptimizationControlMechanism-0 before the " + f"additional Node(s) are added as INPUT Nodes will generate an error.", + + # 7 + f'"The number of \'state_features\' specified for OptimizationControlMechanism-0 (4) is more than the number ' + f'of INPUT Nodes (3) of the Composition assigned as its agent_rep (\'OUTER COMP\')."', + + # 8 + f'The \'state_features\' specified for \'OptimizationControlMechanism-0\' contains items (IA, OC) ' + f'that are not INPUT Nodes of its agent_rep (\'OUTER COMP\'); only INPUT Nodes can be in a set or ' + f'used as keys in a dict used to specify \'state_features\'.', + + # 9 + f'The \'state_features\' specified for \'OptimizationControlMechanism-0\' contains an item (IA) ' + f'that is not an INPUT Node of its agent_rep (\'OUTER COMP\'); only INPUT Nodes can be in a set ' + f'or used as keys in a dict used to specify \'state_features\'.', + + # 10 + f"The '{pnl.STATE_FEATURES}' argument for 'OptimizationControlMechanism-0' includes one or more Compositions " + f"('INNER COMP') in the list specified for its '{pnl.STATE_FEATURES}' argument; these must be replaced by " + f"direct references to the Mechanisms (or their InputPorts) within them to be shadowed.", + + # 11 + f"The '{pnl.STATE_FEATURES}' argument for 'OptimizationControlMechanism-0' includes one or more Compositions " + f"('INNER COMP') in the SHADOW_INPUTS dict specified for its '{pnl.STATE_FEATURES}' argument; these must be " + f"replaced by direct references to the Mechanisms (or their InputPorts) within them to be shadowed.", ] - state_feature_specs = ['partial_legal_ports_spec', - 'full_legal_ports_spec', - 'input_dict_spec', - 'automatic_assignment', - 'shadow_inputs_dict_spec', - 'misplaced_shadow', - 'ext_shadow', - 'ext_output_port', - 'bad_input_format_spec_wrong_shape', - 'bad_input_format_spec_too_many' - ] - state_feature_args = [ - (state_feature_specs[0], messages[0], UserWarning), # partial_legal_ports_spec - (state_feature_specs[1], None, None), # full_legal_ports_spec - (state_feature_specs[2], None, None), # input_dict_spec - (state_feature_specs[3], None, None), # automatic_assignment - (state_feature_specs[4], None, None), # shadow_inputs_dict_spec - (state_feature_specs[5], messages[1], pnl.CompositionError), # misplaced_shadow - (state_feature_specs[6], messages[2], pnl.OptimizationControlMechanismError),# ext_shadow - (state_feature_specs[7], messages[3], pnl.OptimizationControlMechanismError),# ext_output_port - (state_feature_specs[8], messages[4], pnl.OptimizationControlMechanismError),# bad_input_format_spec_wrong_shape - (state_feature_specs[9], messages[5], pnl.OptimizationControlMechanismError) # bad_input_format_spec_too_many + ('partial_legal_list_spec', messages[0], None, UserWarning), + ('full_list_spec', None, None, None), + ('list_spec_with_none', None, None, None), + ('input_dict_spec', None, None, None), + ('input_dict_spec_short', None, None, None), + ('automatic_assignment', None, None, None), + ('shadow_inputs_dict_spec', None, None, None), + ('shadow_inputs_dict_spec_w_none', None, None, None), + ('misplaced_shadow', messages[1], None, pnl.CompositionError), + ('ext_shadow', messages[2], None, pnl.OptimizationControlMechanismError), + ('ext_output_port', messages[3], None, pnl.OptimizationControlMechanismError), + ('input_format_wrong_shape', messages[4], None, pnl.OptimizationControlMechanismError), + ('too_many_inputs_warning', messages[5], None, UserWarning), + ('too_many_w_node_not_in_composition_warning', messages[6], None, UserWarning), + ('too_many_inputs_error', messages[7], None, pnl.OptimizationControlMechanismError), + ('bad_dict_spec_warning', messages[8], None, UserWarning), + ('bad_dict_spec_error', messages[8], None, pnl.OptimizationControlMechanismError), + ('bad_set_spec_warning', messages[0], messages[9], UserWarning), + ('bad_set_spec_error', messages[9], None, pnl.OptimizationControlMechanismError), + ('comp_in_list_spec', messages[10], None, pnl.OptimizationControlMechanismError), + ('comp_in_shadow_inupts_spec', messages[11], None, pnl.OptimizationControlMechanismError) ] @pytest.mark.control @@ -802,19 +882,38 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ocomp.add_linear_processing_pathway([oa,oc]) ocomp.add_linear_processing_pathway([ob,oc]) state_features_dict = { - 'partial_legal_ports_spec': [oa.output_port], - 'full_legal_ports_spec': [ia.input_port, oa.output_port, [3,1,2]], - 'input_dict_spec': {icomp:ia.input_port, oa:oc.input_port, ob:ob.output_port}, + # Legal state_features specifications + 'partial_legal_list_spec': [oa.output_port], + 'full_list_spec': [ia.input_port, oa.output_port, [3,1,2]], + 'list_spec_with_none': [ia.input_port, None, [3,1,2]], + 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK + 'input_dict_spec_short': {oa:oc.input_port, ob:ob.output_port}, # Note: missing oa spec + # 'input_dict_spec': {oa:oc.input_port, ia:ia, ob:ob.output_port}, # <- ia is in nested Comp doesn't work + 'set_spec': {ob, icomp, oa}, # Note: out of order is OK 'automatic_assignment': None, 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, + 'shadow_inputs_dict_spec_w_none': {pnl.SHADOW_INPUTS:[ia, None, ob]}, + # 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[icomp, oa, ob]}, <- BAD SHADOW SPEC + # 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, oc]}, <- OK BECAUSE IT IS JUST FOR SHADOWING + # 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:{ia, oa, ob}}, + # Illegal state_features specifications 'misplaced_shadow':ib.input_port, 'ext_shadow':ext.input_port, 'ext_output_port':ext.output_port, - 'bad_input_format_spec_wrong_shape': [ia.input_port, oa.output_port, oc.output_port], - 'bad_input_format_spec_too_many': [ia.input_port, oa.output_port, ob.output_port, oc.output_port] + 'input_format_wrong_shape': [ia.input_port, oa.output_port, oc.output_port], + 'too_many_inputs_warning': [ia.input_port, oa.output_port, ob.output_port, oc.output_port], + 'too_many_inputs_error': [ia.input_port, oa.output_port, ob.output_port, oc.output_port], + 'too_many_w_node_not_in_composition_warning': [ia, oa, ob, ext], + 'bad_dict_spec_warning': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node + 'bad_dict_spec_error': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node + 'bad_set_spec_warning': {ob, ia}, # elicits both short spec and not INPUT Node warnings (for both ob and ia) + 'bad_set_spec_error': {ob, ia}, # elicits both short spec and not INPUT Node warnings (for both ob and ia) + 'comp_in_list_spec':[icomp, oa.output_port, [3,1,2]], + 'comp_in_shadow_inupts_spec':{pnl.SHADOW_INPUTS:[icomp, oa, ob]} } state_features = state_features_dict[state_feature_args[0]] - message = state_feature_args[1] + message_1 = state_feature_args[1] + message_2 = state_feature_args[2] ocm = pnl.OptimizationControlMechanism(state_features=state_features, objective_mechanism=[ic,ib], function=pnl.GridSearch(), @@ -827,16 +926,41 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ocomp.add_controller(ocm) ocomp.run() - if state_feature_args[0] == 'full_legal_ports_spec': + if state_feature_args[0] == 'full_list_spec': assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA', 'OA[OutputPort-0]', 'OB'] + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'OA[OutputPort-0]', + 'OB DEFAULT_VARIABLE'] assert ocm.state_features == {icomp: ia.input_port, oa: oa.output_port, ob: [3, 1, 2]} + if state_feature_args[0] == 'list_spec_with_none': + assert len(ocm.state_input_ports) == 2 + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'OB DEFAULT_VARIABLE'] + assert ocm.state_features == {icomp: ia.input_port, ob: [3, 1, 2]} + elif state_feature_args[0] == 'input_dict_spec': assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['INNER COMP', 'OA', 'OB'] + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OC[InputPort-0]', + 'OB[OutputPort-0]'] + # 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK assert ocm.state_features == {icomp:ia.input_port, oa:oc.input_port, ob:ob.output_port} + elif state_feature_args[0] == 'input_dict_spec_short': + assert len(ocm.state_input_ports) == 2 + assert ocm.state_input_ports.names == ['Shadowed input of OC[InputPort-0]', + 'OB[OutputPort-0]'] + assert ocm.state_features == {oa:oc.input_port, ob:ob.output_port} + + elif state_feature_args[0] == 'set_spec': + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OA[InputPort-0]', + 'Shadowed input of OB[InputPort-0]'] + # 'set_spec': {ob, icomp, oa}, # Note: out of order is OK + assert ocm.state_features == {icomp:ia.input_port, oa:oa.input_port, ob:ob.input_port} + elif state_feature_args[0] == 'automatic_assignment': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', @@ -844,22 +968,46 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'Shadowed input of OB[InputPort-0]'] assert ocm.state_features == {icomp: ia.input_port, oa: oa.input_port, ob: ob.input_port} - elif state_feature_args[2] is UserWarning: - with pytest.warns(UserWarning) as warning: - ocomp.add_controller(ocm) - ocomp.run() - if state_feature_args[0] == 'partial_legal_ports_spec': - assert len(ocm.state_input_ports) == 1 - assert ocm.state_input_ports.names == ['OA[OutputPort-0]'] - assert ocm.state_features == {icomp: oa.output_port} - assert warning[10].message.args[0] == message - assert ocm.state_features == {icomp: oa.output_port} + elif state_feature_args[0] == 'shadow_inputs_dict_spec': + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OA[InputPort-0]', + 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_features == {icomp: ia.input_port, oa: oa.input_port, ob: ob.input_port} + + elif state_feature_args[0] == 'shadow_inputs_dict_spec_w_none': + assert len(ocm.state_input_ports) == 2 + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_features == {icomp: ia.input_port, ob: ob.input_port} + + elif state_feature_args[3] is UserWarning: + # These also produce errors, tested below + if state_feature_args[0] in {'too_many_inputs_warning', + 'too_many_w_node_not_in_composition_warning', + 'bad_dict_spec_warning', + 'bad_set_spec_warning'}: + with pytest.warns(UserWarning) as warning: + ocomp.add_controller(ocm) + assert warning[0].message.args[0] == message_1 + if state_feature_args[0] in 'bad_set_spec_warning': + assert message_2 == warning[1].message.args[0] # since set, order of ob and ia is not reliable + else: + with pytest.warns(UserWarning) as warning: + ocomp.add_controller(ocm) + ocomp.run() + if state_feature_args[0] == 'partial_legal_list_spec': + assert len(ocm.state_input_ports) == 1 + assert ocm.state_input_ports.names == ['OA[OutputPort-0]'] + assert ocm.state_features == {icomp: oa.output_port} # Note: oa is assigned to icomp due to ordering + assert warning[0].message.args[0] == message_1 + assert ocm.state_features == {icomp: oa.output_port} else: - with pytest.raises(state_feature_args[2]) as error: + with pytest.raises(state_feature_args[3]) as error: ocomp.add_controller(ocm) ocomp.run() - assert message in str(error.value) + assert message_1 in str(error.value) @pytest.mark.control def test_ocm_state_and_state_dict(self): @@ -2867,7 +3015,7 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ # Test args: if nested_agent_rep is True: agent_rep = mcomp - error_text = f"'OCM' has 'state_features' specified (['D[OutputPort-0]']) that are missing from both " \ + error_text = f"'OCM' has '{STATE_FEATURES}' specified (['D[OutputPort-0]']) that are missing from both " \ f"its `agent_rep` ('{nested_agent_rep[1]}') as well as 'OUTER COMP' and any " \ f"Compositions nested within it." else: diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index 108a5a937d1..e69dbc94407 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -448,6 +448,7 @@ def test_nested_learning_test_with_user_specified_target_in_outer_composition( assert len(target.efferents) == 1 assert target.efferents[0].receiver == icomp.input_CIM.input_ports['INPUT_CIM_Target_InputPort-0'] assert icomp.input_CIM.output_ports['INPUT_CIM_Target_InputPort-0'].efferents[0].receiver.owner == p.target + assert ocomp.controller.num_state_input_ports == 1 gv = ocomp.show_graph(output_fmt='source', **show_graph_kwargs) assert gv.strip() == expected_output @@ -671,20 +672,20 @@ def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer # each item corresponds to the same item in _nested_show_graph_kwargs above _nested_comp_to_ocm_or_obj_mech = [ - # # # monitor_for_control: + # monitor_for_control: 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tib -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', # THE FOLLOWING IS INCORRECT (SEE COMMENTS IN TEST) - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', # obj_mech 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\tib -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\tib -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', @@ -692,16 +693,16 @@ def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', # THE FOLLOWING IS INCORRECT (SEE COMMENTS IN TEST) - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', ] num_show_graph_combos = len(_nested_show_graph_kwargs) - obj_mech = ['monitor_for_control'] * num_show_graph_combos + ['obj_mech'] * num_show_graph_combos + obj_mech = ['monitor_for_control'] * num_show_graph_combos + ['obj_mech'] * num_show_graph_combos show_graph_args = list(zip(_nested_show_graph_kwargs * 2, _nested_comp_to_ocm_or_obj_mech)) test_args = [] ids = [] @@ -756,7 +757,8 @@ def test_projections_from_nested_comp_to_ocm_or_obj_mech(self, show_graph_kwargs # ??SAME BUG AS IN MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c ??) if ('show_node_structure' in show_graph_kwargs and 'show_cim' not in show_graph_kwargs - and ('show_nested' in show_graph_kwargs and show_graph_kwargs['show_nested']==NESTED) + and ('show_nested' in show_graph_kwargs + and show_graph_kwargs['show_nested'] == NESTED) ): # The test in this condition is for incorrect show_graph output due an as yet undetermined bug # in which the Projection from the OCM receives a projection to its control_signal output_port @@ -765,6 +767,13 @@ def test_projections_from_nested_comp_to_ocm_or_obj_mech(self, show_graph_kwargs # If the test fails in this condition, it could mean that the bug has been corrected. # The bug may be the same one as in eb61303808ad2a5ba46fdd18d0e583283397915c raise(AssertionError,"FAILURE TO REPLICATE BUGGY SHOW_GRAPH OUTPUT -- SEE COMMENT IN TEST") + # elif ('show_node_structure' in show_graph_kwargs + # and ('show_cim' in show_graph_kwargs + # and show_graph_kwargs['show_cim'] is True) + # and ('show_nested' in show_graph_kwargs + # and show_graph_kwargs['show_nested'] == INSET) + # ): + # pass else: raise(AssertionError) From 3458e643d59fcc1c9b988616e7e8bbece0df50f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jan 2022 04:26:38 +0000 Subject: [PATCH 116/285] requirements: update matplotlib requirement from <3.4.4 to <3.5.2 (#2238) --- requirements.txt | 2 +- tutorial_requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4423e0391c1..0ab1e961c16 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ graphviz<0.20.0 grpcio<1.43.0 grpcio-tools<1.43.0 llvmlite<0.39 -matplotlib<3.4.4 +matplotlib<3.5.2 networkx<2.6 numpy<1.21.4, >=1.17.0 pillow<9.1.0 diff --git a/tutorial_requirements.txt b/tutorial_requirements.txt index 98ae285469c..728aa0c0eab 100644 --- a/tutorial_requirements.txt +++ b/tutorial_requirements.txt @@ -1,3 +1,3 @@ graphviz<0.20.0 jupyter<=1.0.0 -matplotlib<3.4.4 +matplotlib<3.5.2 From 3a8bf9dec12753e9524c499d666b9c38d60f4a93 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 30 Jan 2022 23:04:40 -0500 Subject: [PATCH 117/285] Feat/ocm/state features (#2301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - _update_state_input_ports_for_controller: - skip if ContextFlags.METHOD -> ContextFlags.PROCESSING * • optimizationcontrolmechanism.py: - add _validate_state_features - _update_state_input_ports_for_controller: validation of state_features moved to _validate_state_features * - * • optimizationcontrolmechanism.py - Parameters: add outcome_input_ports and state_input_ports * • controlmechanism.py & optimizationcontrolmechanism.py - move outcome_input_ports Parameter from ocm to controlmechanism - state_features: set to make Parameter on ocm, but waiting to deal with LLVM * • controlmechanism.py & optimizationcontrolmechanism.py - move outcome_input_ports Parameter from ocm to controlmechanism - state_features: set to make Parameter on ocm, but waiting to deal with LLVM * Passing all tests but ones in test_show_graph and predator_prey * • test_show_graph.py: - correct output for fix of typo • test_greedy_agent.py: - test_predator_prey(): reorder args in state_features to fix failure * - * - * - * • inputport.py: - _parse_self_port_type_spec(): change name of shadowed inputports to use full_name * - * - * - * - * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): - convert list spec to dict - handle nested Comp INPUT Node in set spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): passes all tests * - * - * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): errors for comp spec in list or SHADOW_INPUTS dict (should be replaced by expansion to INPUT Nodes of comp • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add tests for comp spec in list or SHADOW_INPUTS dict (replace with checks when expansion of these is handled) * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order) *** TBI state_features needs to be modified to accomodate this *** • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add test for None in list spec * • optimizationcontrolmechanism.py state_features: use self._input_nodes_for_state_features_specs * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order) *** TBI state_features needs to be modified to accomodate this *** • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add test for None in list spec * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order): fully implemented TBI: implement for SHADOW_INPUTS list spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): passes all tests * • optimizationcontrolmechanism.py: - move warning for specs < INPUT Nodes from _validate_state_features() to _parse_state_feature_specs() * • optimizationcontrolmechanism.py: - move warning for specs < INPUT Nodes from _validate_state_features() to _parse_state_feature_specs() * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): handle None in SHADOW_INPUTS spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors: - properly handle shadow_inputs_dict_spec and shadow_inputs_dict_spec_w_none conditions * Passes all test_ocm_state_feature_specs_and_warnings_and_errors tests, but uses internal dict that fails for deferred_init of agent_rep, since INPUT nodes (used as keys in dict) may not yet have been constructed. * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): refactored to use parallel lists rather than dict for nodes and specs • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - all tests pass * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): still needs to deal with CFA * - * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • optimizationcontrolmechanism.py _parse_state_feature_specs(): - suppress warning for more state_features than INPUT Nodes in agent_rep (duplicates exception raised in _validate_state_features -- but see comment) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • composition.py - run(): add context.execution_phase = ContextFlags.PREPARING at beginning of method * - * - * MODS TO DEFER VALIDATION OF state_features UNTIL RUN TIME (AFTER Composition IS FULLY CONSTRUCTED) • composition.py: - run(): - set context.execution_phase to ContextFlags.PREPARING at start and reinstate entry context just before executing trials - _complete_init_of_partially_initialized_nodes(): - set needs_update_controller based on return value of ocm._update_state_input_ports_for_controller() • optmizationcontrolmechanism.py: _ _update_state_input_ports_for_controller(): - restrict call to _validate_state_features() to run time (ContextFlags.PROCESSING) - return True if passes, None otherwise - _validate_state_features(): - reinstate warning for number of state_features > agent_rep INPUT Nodes • test_control.py: -test_args_specific_to_ocm() - add run() to test to preciptitate error detected in _validate_state_feature_specs() - test_ocm_state_feature_specs_and_warnings_and_errors(): - reinstate 'too_many_inputs_warning' condition * - * - * • optimizationcontrolmechanism.py: deal with deferred INPUT nodes in state_features * - * - * • Passes all tests in test_ocm_state_feature_specs_and_warnings_and_errors() * - * - * • optimizationcontrolmechanism.py: - add _validate_input_nodes() - add _instantiate_pending_state_features() - STUB at present * - * - * • Passes all tests * • optimizationcontrolmechanism.py - state_features: updated after adding nodes to comp * - * • Passes all tests • optimizationcontrolmechanism.py - state_features: correct to properly include updated INPUT nodes * • GENUNINELY Passes all tests (some were commented out on the prior commit) • optimizationcontrolmechanism.py - _parse_state_feature_specs() & state_features: further fixes to properly include added INPUT nodes * - * - * - * • optimizationcontrolmechanism.py _parse_state_feature_specs() & _validate_state_features(): add mention of missing nodes to warning / error messages * • optimizationcontrolmechanism.py add _get_nodes_not_in_agent_rep() * - * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): enforce list spec for agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR * • composition.py: - add_node(): add call to _analyze_graph if not called from another Composition add method * - * • composition.py - add_node(): - comment out call to _analyze_graph: crashes in test_learning_output_shape() with ExecuteMode.LLVM • optimizationcontrolmechanism.py - state_features: clean up * • optimizationcontrolmechanism.py - _parse_state_feature_sepcs(): - removed argss: directly reference self.state_feature_specs and self.state_feature_functions * - * - * - * • optimizationcontrolmechanism.py: - _parse_state_feature_values_from_variable() - move to method on Class - populate with default variable for INPUT Nodes not specified as state_features * • optimizationcontrolmechanism.py: - remove _parse_state_feature_values_from_variable(): removed - add _state_feature_values_getter() * - * BEFORE REFACTORING TO INCLUDE ALL INPUT Nodes IN state_features (WITH VALUE OF NONE FOR ONES NOT SPECIFIED IN state_feature_specs) • optimizationcontrolmechanism.py - _state_feature_values_getter(): modify to handle calls in init and validation * PARTIAL REFACTORING TO INCLUDE ALL INPUT Nodes IN state_features (WITH VALUE OF NONE FOR ONES NOT SPECIFIED IN state_feature_specs) * REFACTORED TO INCLUDE ALL INPUT Nodes IN state_features (WITH VALUE OF NONE FOR ONES NOT SPECIFIED IN state_feature_specs) - PASSING test_ocm_state_feature_specs_and_warnings_and_errors() - WORKING on test_partial_deferred_init() * PASSES ALL test_control except lvoc * PASSES ALL TESTS • optimizationcontrolmechanism.py: - Mark potential mods for making state_feature_specs a Parameter (marked with 1/30/22) (with self.state_feature_specs that preserves user specifications) - implement self._state_feature_specs_parsed (in place of state_feature_specs Parameter for now) - restore self._specified_input_nodes_in_order (in place of state_feature_specs Parameter for now) - state.features property constructs dict from above attributes * PASSES ALL TESTS • optimizationcontrolmechanism.py: - Mark potential mods for making state_feature_specs a Parameter (marked with 1/30/22) (with self.state_feature_specs that preserves user specifications) - implement self._state_feature_specs_parsed (in place of state_feature_specs Parameter for now) - restore self._specified_input_nodes_in_order (in place of state_feature_specs Parameter for now) - state.features property constructs dict from above attributes * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add tests for state_feature_values * • optimizationcontrolmechanism.py - docstring updates * • optimizationcontrolmechanism.py - docstring updates Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- conftest.py | 6 +- .../control/optimizationcontrolmechanism.py | 669 ++++++++++-------- psyneulink/core/compositions/composition.py | 16 +- psyneulink/core/globals/parameters.py | 4 + tests/composition/test_control.py | 107 ++- 5 files changed, 487 insertions(+), 315 deletions(-) diff --git a/conftest.py b/conftest.py index 92c98215b47..8521ae6c014 100644 --- a/conftest.py +++ b/conftest.py @@ -68,8 +68,10 @@ def pytest_generate_tests(metafunc): metafunc.parametrize("comp_mode", get_comp_execution_modes()) if "autodiff_mode" in metafunc.fixturenames: - auto_modes = [pnlvm.ExecutionMode.Python, - pytest.param(pnlvm.ExecutionMode.LLVMRun, marks=pytest.mark.llvm)] + auto_modes = [ + pnlvm.ExecutionMode.Python, + pytest.param(pnlvm.ExecutionMode.LLVMRun, marks=pytest.mark.llvm) + ] metafunc.parametrize("autodiff_mode", auto_modes) def pytest_runtest_call(item): diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 8ca8c99fc2c..45114fe0352 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -11,7 +11,6 @@ # FIX: REWORK WITH REFERENCES TO `outcome ` # INTRODUCE SIMULATION INTO DISCUSSION OF COMPOSITION-BASED - """ Contents @@ -20,11 +19,13 @@ * `OptimizationControlMechanism_Overview` - `Expected Value of Control ` - `Agent Representation and Types of Optimization ` - - `Model-Free" Optimization ` - - `Model-Based" Optimization ` + - `"Model-Free" Optimization ` + - `Model-Based Optimization ` * `OptimizationControlMechanism_Creation` - `Agent Rep ` - `State Features ` + - `agent_rep Composition ` + - `agent_rep CompositionFunctionApproximator ` - `State Feature Functions ` - `Outcome ` * `OptimizationControlMechanism_Structure` @@ -42,6 +43,7 @@ - `Output ` - `Randomization ControlSignal ` * `OptimizationControlMechanism_Execution` + - `OptimizationControlMechanism_Execution_Timing` - `OptimizationControlMechanism_Optimization_Procedure` - `OptimizationControlMechanism_Estimation_Randomization` * `OptimizationControlMechanism_Class_Reference` @@ -210,114 +212,126 @@ .. _OptimizationControlMechanism_State_Features_Arg: -* **state_features** -- specifies the values provided by the OptimizationControlMechanism as the input to the - `agent_rep ` when used, together with a selected `control_allocation - `, to estimate or predict the Composition's `net_outcome - `. These are used to construct the `state_input_ports - ` for the OptimizationControlMechanism, the `values ` - of which are assigned to `state_feature_values ` and provided to - the `agent_rep ` as its input when it is evaluated. Accordingly, the - specification requirements for **state_features** depend on whether the - `agent_rep` is a `Composition` or a `CompositionFunctionApproximator`: +* **state_features** -- specifies the values provided by the OptimizationControlMechanism as the input to its + `agent_rep `\\'s `evaluate ` method, together + with a selected `control_allocation `, when that is called to estimate + or predict the Composition's `net_outcome `. These are used to construct the + `state_input_ports ` for the OptimizationControlMechanism, + the `values ` of which are assigned as the `state_feature_values + ` and provided to the **predicted_inputs** argument of the + `evaluate ` method if `agent_rep ` is a `Composition`, + or the **feature_values** argument if it is a `CompositionFunctionApproximator`. Accordingly, the specification + requirements for **state_features** depend on whether the `agent_rep` is a + `Composition` or a `CompositionFunctionApproximator`, as described in each of the two sections below. + + | .. _OptimizationControlMechanism_Agent_Rep_Composition: - * *agent_rep is a Composition* -- the **state_features** specify the inputs to the Composition when it is executed - by the OptimizationControlMechanism to `evaluate ` its performance. - If **state_features** is not specified, this is done automatically by constructing a set of `state_input_ports - ` that `shadow the input ` to every - `InputPort` of every `INPUT ` `Node ` of the Composition assigned as - the `agent_rep `. In this case, if `controller_mode - ` of the Composition for which the OptimizationControlMechanism is the `controller - ` is set to *AFTER* (the default), the `input ` to - the Composition on the current trial is used as its input to the `agent_rep - ` for the optimization process; if the `controller_mode - ` is *BEFORE*, then the inputs from the previous trial are used. - - .. _OptimizationControlMechanism_State_Features_Explicit_Specification: - - The **state_features** argument can also be specified explicitly, using the formats described below. This is - useful if the values of the `state_input_ports ` are something - other than the inputs to the `agent_rep `, or if different functions need - to be assigned to different `state_input_ports ` used to generate - the corresponding `state_feature_values state_feature_values ` - (see `below `). However, doing so overrides the automatic - assignment of all state_features, and so a complete and appropriate set of specifications must be provided - (see note below). - - .. _OptimizationControlMechanism_State_Features_Shapes: + **state_features** for an agent_rep that is a* **Composition** + + | + + The **state_features** specify the inputs to the Composition assigned as the `agent_rep + ` when it is executed by the OptimizationControlMechanism to + `evaluate ` its performance. The default is for the evaluation to use the + same values received by the `agent_rep ` as its `external inputs + ` during its last `TRIAL ` of execution. Accordingly, if + **state_features** is not specified, a set of `state_input_ports ` + is constructed automatically that `shadow the input ` to every `InputPort` of every + `INPUT ` `Node ` of the `agent_rep ` + Composition. + + | + + .. _OptimizationControlMechanism_State_Features_Explicit_Specification: + The **state_features** argument can also be specified explicitly, using the formats described below. This is + useful if values other than the `external inputs ` to the `agent_rep + ` Composition are to be used to evaluate it, to restrict evaluation + This allows values other than the `external inputs ` to the `agent_rep + ` Composition to be used to evaluate it; to restrict evaluation to a + subset of inputs (while others are held constant); and/or to assign specific functions to one or more + `state_input_ports ` that allow them to process inputs + (e.g., modulate and/or intergrate them) before them as `state_feature_values state_feature_values + ` (see `below + `). Note that assigning **state_features** explicilty + overrides their automatic assignment, so that all required values must be specified, and this must be done + accurate, as described below. + + .. _OptimizationControlMechanism_State_Features_Shapes: - .. note:: - If **state_features** are specified explicitly when the `agent_rep ` - is a Composition, there must be one for every `InputPort` of every `INPUT ` `Node - ` in that Composition, and these must match -- both individually, and in their order -- - the `inputs to the Composition `) required by its `run ` - method. Failure to do so generates an error. - - .. _OptimizationControlMechanism_Selective_Input: - - .. hint:: - For cases in which only a subset of the inputs to the Composition are relevant to its optimization (e.g., - the others should be held constant), it is still the case that all must be specified as **state_features** - (see note above). This can be handled several ways. One is by specifying (as required) **state_features** - for all of the inputs, and assigning *state_feature_functions** (see `below - `) such that those assigned to the desired - inputs pass their values unmodified, while those for the inputs that are to be ignored return a constant value. - Another approach, for cases in which the desired inputs pertain to a subset of Components in the Composition - solely responsible for determining its `net_outcome `, is to assign those - Components to a `nested Composition ` and assign that Composition as the `agent_rep - `. A third, more sophisticated approach, is to assign - ControlSignals to the InputPorts for the irrelevant features, and specify them to suppress their values. - - .. _OptimizationControlMechanism_Agent_Rep_CFA: - - * *agent_rep is a CompositionFunctionApproximator* -- the **state_features** specify the inputs to the - CompositionFunctionApproximator's `evaluate ` method. This is not - done automatically (see warning below). + .. note:: + If **state_features** are specified explicitly, the shapes of the `value `\\s of the + specified Components must match those required as `external inputs ` to + the corresponding `INPUT ` `Nodes ` of the `agent_rep + `. An example of the input format required by the `INPUT + ` `Nodes ` can be generated using the `agent_rep + `\\'s `get_input_format ` method. + A failure to properly meet these requirements generates an error. - .. warning:: - The **state_features** specified when the `agent_rep ` - is a `CompositionFunctionApproximator` must align with the arguments of its `evaluate - ` method. Since the latter cannot always be determined - automatically, the `state_input_ports ` cannot be created - automatically, nor can the **state_features** specification be validated; thus, specifying inappropriate - **state_features** may produce errors that are unexpected or difficult to interpret. + COMMENT: + .. _OptimizationControlMechanism_Selective_Input: - COMMENT: - FIX: CONFIRM (OR IMPLEMENT?) THE FOLLOWING - If all of the inputs to the Composition are still required, these can be specified using the keyword *INPUTS*, - in which case they are retained along with any others specified. - COMMENT + .. hint:: + For cases in which only a subset of the inputs to the Composition are relevant to its optimization (e.g., + the others should be held constant), it is still the case that all must be specified as **state_features** + (see note above). This can be handled several ways. One is by specifying (as required) **state_features** + for all of the inputs, and assigning *state_feature_functions** (see `below + `) such that those assigned to the desired + inputs pass their values unmodified, while those for the inputs that are to be ignored return a constant value. + Another approach, for cases in which the desired inputs pertain to a subset of Components in the Composition + solely responsible for determining its `net_outcome `, is to assign those + Components to a `nested Composition ` and assign that Composition as the `agent_rep + `. A third, more sophisticated approach, is to assign + ControlSignals to the InputPorts for the irrelevant features, and specify them to suppress their values. + COMMENT .. _OptimizationControlMechanism_State_Features_Shadow_Inputs: The specifications in the **state_features** argument are used to construct the `state_input_ports - `. As noted - `above `, specifying these explicitly overrides + `. As noted `above + `, specifying these explicitly overrides their automatic construction. They can be specified using any of the following: .. _Optimization_Control_Mechanism_State_Feature_Input_Dict: - * *Inputs dictionary* -- a dictionary that conforms to the format used to `specify inputs + * *Inputs dictionary* -- a dictionary that conforms to the format used to `specify external inputs ` to the `agent_rep `, in which entries consist of a key specifying an `INPUT ` Node of `agent_rep `, and its value is the source of the input, that can be any of the forms - of individual input specifications listed `below `. - The full format required for inputs to `agent_rep ` can be seen using - its `get_input_format ` method. If only some `INPUT ` Nodes are - specified, the remaining ones are assigned their `default values ` when the `agent_rep - `\\'s `evaluate ` method is called. - This is the most reliable and straightforward way to specify **state_features**. + of individual input specifications listed `below + `. This is the most straightforward and reliable + way to specify **state_features**. The full format required for inputs to `agent_rep + ` can be seen using its `get_input_format ` + method. If only some `INPUT ` Nodes are specified, the remaining ones are assigned their + `default variable ` as input when the `agent_rep `\\'s + `evaluate ` method is called, irrespective of the input to the `agent_rep + ` during the last `TRIAL `. .. _Optimization_Control_Mechanism_State_Feature_List_Inputs: * *List* -- a list of individual input source specifications, that can be any of the forms of individual input - specifications listed `below `. These are assumed - to be listed in the order that `INPUT ` Nodes are listed in the of the `nodes ` - attribute of `agent_rep ` (and returned by a call to its + specifications listed `below `. The items + must be listed in the order that `INPUT ` Nodes are listed in the `agent_rep + `\\'s `nodes ` attribute (and returned by its `get_nodes_by_role(NodeRole.INPUT) ` method). If the list is incomplete, - the remaining ones are assigned their `default values ` when the `agent_rep - `\\'s `evaluate ` method is called. + the remaining INPUT Nodes are assigned their `default variable ` as input when the `agent_rep + `\\'s `evaluate ` method is called; ``None`` can + be used as an entry to "skip" items in the list (i.e., specify that they receive their `default variable + ` as input). + + .. _Optimization_Control_Mechanism_State_Feature_Set_Inputs: + + * *Set* -- a set of `INPUT ` `Nodes ` of the `agent_rep + ` to receive the same inputs during evaluation as when + the `agent_rep ` is fully executed; the `state_input_ports + ` constructed for these state_features are assigned + Projections that `shadow ` the specified `INPUT ` `Node + ` of the `agent_rep `. The order of their + specification does not matter; however, any of the `agent_rep `\\'s + `INPUT ` Nodes that are *not* included in the set will be assigned their `default variable + ` when the `agent_rep `\\'s `evaluate + ` method is called. .. _Optimization_Control_Mechanism_State_Feature_Individual_Inputs: @@ -328,14 +342,16 @@ ` `Node ` of the `agent_rep ` when it is `evaluated ` method is called. - .. note:: - If only a single input specification is provided to **state_features**, it is treated as a list with a single - item (see `above `), and assigned as the input to the - first `INPUT ` Node of `agent_rep `; if the latter has - any additional `INPUT ` Nodes, they are assigned their `default values `. + .. note:: + If only a single input specification is provided to **state_features**, it is treated as a list with a single + item (see `above `), and assigned as the input to the + first `INPUT ` Node of `agent_rep `; if the latter has + any additional `INPUT ` Nodes, they are assigned their `default variable ` + as inputs when the `agent_rep `\\'s `evaluate ` + method is executed. .. _Optimization_Control_Mechanism_Numeric_State_Feature: - * *numeric value* -- create an `InputPort` with the specified value as its `default value ` + * *numeric value* -- create an `InputPort` with the specified value as its `default variable ` and no `afferent Projections `; as a result, the specified value is assigned as the input to the corresponding `INPUT ` `Node ` of the `agent_rep ` each time it is `evaluated `. @@ -346,15 +362,18 @@ `state_input_port ` (see `state feature functions ` for additional details). - .. _Optimization_Control_Mechanism_Tuple_State_Feature: - * *specification dictionary* -- an `InputPort specification dictionary ` can be - used to configure the corresponding `state_input_port `, if - `Parameters ` other than its `function ` need to be specified (e.g., its `name + COMMENT: + FIX: CONFIRM THAT THIS WORKS + .. _Optimization_Control_Mechanism_Input_Port_Dict_State_Feature: + * *specification dictionary* -- an `InputPort specification dictionary ` + can be used to configure the corresponding `state_input_port `, + if `Parameters ` other than its `function ` need to be specified (e.g., its `name ` or more than a single `afferent Projection `). + COMMENT .. _Optimization_Control_Mechanism_Input_Port_State_Feature: - * *InputPort specification* -- create an `InputPort` that `shadows ` the - input to the specified InputPort, ,the value of which is used as the corresponding value of the + * *InputPort specification* -- creates an `InputPort` that `shadows ` the input to + the specified InputPort, the `value ` of which is used as the corresponding value of the OptimizationControlMechanism's `state_feature_values `. .. note:: @@ -398,6 +417,25 @@ Mechanism *is* to be shadowed, then its InputPort must be specified explicitly (as described `above `). + | + + .. _OptimizationControlMechanism_Agent_Rep_CFA: + + **state_features** *for an agent_rep that is a* **CompositionFunctionApproximator** + + | + + **state_features** specify the **feature_values** + argument to the CompositionFunctionApproximator's `evaluate ` method. + These cannot be determined automatically and so they *must be specified explicity*, in a list, with the correct + number of items in the same order and with the same shapes they are expected have in the array passed to the + **feature_values** argument of the `evaluate` method (see warning below). + .. warning:: + The **state_features** for an `agent_rep ` that is a + `CompositionFunctionApproximator` cannot be created automatically nor can they be validated; + thus specifying the wrong number or invalid **state_features**, or specifying them in an incorrect + order may produce errors that are unexpected or difficult to interpret. + COMMENT: FIX: CONFIRM THAT THE FOLLOWING ALL WORK State features can also be added to an existing OptimizationControlMechanism using its `add_state_features` method. @@ -521,8 +559,8 @@ ` -- is determined by the OptimizationControlMechanism's current `state_feature_values ` (see `below `) and `control_allocation `. -These are provided as input to the `evaluate_agent_rep ` method, -the results of which are used together with the `costs ` associated with the +These are used by the `evaluate_agent_rep ` method, +the results of which are combined with the `costs ` associated with the `control_allocation `, to evaluate the `net_outcome ` for that state. The current state is listed in the OptimizationControlMechanism's `state ` attribute, and `state_dict ` @@ -552,7 +590,7 @@ `, the values of which are assigned as the `state_feature_values `, and conveyed to the `agent_rep ` when it is `executed `. If the -`agent_rep is a `Composition `, then the +`agent_rep is a Composition `, then the OptimizationControlMechanism has a state_input_port for every `InputPort` of every `INPUT ` `Node ` of the `agent_rep ` Composition, each of which receives a `Projection` that `shadows the input ` of the corresponding state_feature. If the @@ -790,6 +828,20 @@ of the Composition or executing the CompositionFunctionApproximator that is its `agent_rep `. +.. _OptimizationControlMechanism_Execution_Timing: + +*Timing of Execution* +^^^^^^^^^^^^^^^^^^^^^ + +When the OptimizationControlMechanism is executed is determined by the `controller_mode ` +of the Composition for which the OptimizationControlMechanism is the `controller `: if it is +set to *AFTER* (the default), the OptimizationControlMechanism is executed at the end of a `TRIAL `, +after the Composition has executed, using `state_feature_value ` +(including any inputs to the Composition) for that `TRIAL `; if the `controller_mode +` is *BEFORE*, then the OptimizationControlMechanism is executed before the Composition +that it controls, using `state_feature_value ` (including any inputs +to the Composition) from the previous `TRIAL `. + .. _OptimizationControlMechanism_Optimization_Procedure: *Optimization Procedure* @@ -824,11 +876,14 @@ ` for that `control_allocation `. It does this by calling the OptimizationControlMechanism's `evaluate_agent_rep ` method `num_estimates ` times, - each with the current `state_feature_values ` as its input, - and executing it for `num_trials_per_estimate ` trials - for each estimate. The `control_allocation ` remains fixed for each - estimate, but the random seed of any Parameters that rely on randomization is varied, so that the values of those - Parameters are randomly sampled for every estimate (see `OptimizationControlMechanism_Estimation_Randomization`). + each of which uses the `state_feature_values ` and + `control_allocation ` as the input to the `agent_rep + `\\'s `evaluate ` method, executing it for + `num_trials_per_estimate ` trials for each estimate. + The `state_feature_values ` and `control_allocation + ` remain fixed for each estimate, but the random seeds of any Parameters + that rely on randomization are varied, so that the values of those Parameters are randomly sampled for every + estimate (see `OptimizationControlMechanism_Estimation_Randomization`). * *Aggregation* - the `function `\\'s `aggregation_function ` is used to aggregate the `net_outcome @@ -964,9 +1019,38 @@ RANDOM_VARIABLES = 'random_variables' NUM_ESTIMATES = 'num_estimates' -def _parse_state_feature_values_from_variable(index, variable): - """Return values of state_input_ports""" - return convert_to_np_array(np.array(variable[index:]).tolist()) +def _state_feature_values_getter(owning_component=None, context=None): + # If no state_input_ports return empty list + if (not owning_component.num_state_input_ports): + return [] + # If OptimizationControlMechanism is still under construction, use items from input_values as placemarkers + elif context.source == ContextFlags.CONSTRUCTOR: + return owning_component.input_values[owning_component.num_outcome_input_ports:] + + # Otherwise, use current values of state_input_ports + state_input_port_values = [p.parameters.value.get(context) for p in owning_component.state_input_ports] + + if (not owning_component.state_feature_specs + or owning_component.num_state_input_ports == len(owning_component.state_feature_specs)): + # Automatically assigned state_features or full set of specs for all INPUT Nodes, + # so use values of state_input_ports (since there is one for every INPUT Node of agent_rep) + state_feature_values = state_input_port_values + else: + # Specified state_features for a subset of INPUT Nodes so use those + j = 0 + state_feature_values = [] + for node, spec in zip(owning_component._specified_input_nodes_in_order, + owning_component._state_feature_specs_parsed): + if spec is not None: + state_feature_values.append(state_input_port_values[j]) + j += 1 + else: + # FIX: 1/29/22 - HANDLE VARIBLE AS 1d vs 2d + # assert node.defaults.variable.ndim == 2 and len(node.defaults.variable)==1 + # state_feature_values.append(node.defaults.variable[0]) + state_feature_values.append(node.defaults.variable) + + return convert_to_np_array(state_feature_values) class OptimizationControlMechanismError(Exception): def __init__(self, error_value): @@ -1420,9 +1504,9 @@ class Parameters(ControlMechanism.Parameters): """ outcome_input_ports_option = Parameter(CONCATENATE, stateful=False, loggable=False, structural=True) state_input_ports = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) - # state_features = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) - function = Parameter(GridSearch, stateful=False, loggable=False) + # state_feature_specs = Parameter(None, stateful=False, loggable=False, read_only=True, structural=True) state_feature_functions = Parameter(None, reference=True, stateful=False, loggable=False) + function = Parameter(GridSearch, stateful=False, loggable=False) search_function = Parameter(None, stateful=False, loggable=False) search_space = Parameter(None, read_only=True) search_termination_function = Parameter(None, stateful=False, loggable=False) @@ -1431,10 +1515,7 @@ class Parameters(ControlMechanism.Parameters): agent_rep = Parameter(None, stateful=False, loggable=False, pnl_internal=True, structural=True) - # FIX: NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED (CHANGE 1 to 0? IF STATE_INPUT_PORTS ARE FIRST) - state_feature_values = Parameter(_parse_state_feature_values_from_variable(1, [defaultControlAllocation]), - user=False, - pnl_internal=True) + state_feature_values = Parameter(None, getter=_state_feature_values_getter, user=False, pnl_internal=True) # FIX: Should any of these be stateful? random_variables = ALL @@ -1454,6 +1535,11 @@ class Parameters(ControlMechanism.Parameters): saved_samples = None saved_values = None + # # MODIFIED 1/30/22 NEW: MAY BE NEEDED IF state_feature_specs IS MADE A PARAMETER; SHOULD SET spec ATTRIBUTE + # def _parse_state_feature_specs(self, state_features): + # return (state_features if isinstance(state_features, (dict, set)) else convert_to_list(state_features)) + # MODIFIED 1/30/22 END + @handle_external_context() @tc.typecheck def __init__(self, @@ -1499,8 +1585,10 @@ def __init__(self, kwargs.pop('feature_function') continue + # MODIFIED 1/30/22 OLD: REMOVE IF state_feature_specs -> Parameter AND SET IN Parameter._parse_state_feature_specs self.state_feature_specs = (state_features if isinstance(state_features, (dict, set)) else convert_to_list(state_features)) + # MODIFIED 1/30/22 END function = function or GridSearch @@ -1536,7 +1624,9 @@ def __init__(self, super().__init__( agent_rep=agent_rep, - state_features=state_features, + # # MODIFIED 1/30/22 NEW: MAY NEED IF state_feature_specs -> Parameter + # state_feature_specs=state_features, + # MODIFIED 1/30/22 END state_feature_functions=state_feature_functions, function=function, num_estimates=num_estimates, @@ -1630,7 +1720,6 @@ def _instantiate_input_ports(self, context=None): # If any state_features were specified parse them and pass to ControlMechanism._instantiate_input_ports() state_input_ports_specs = None - self._specified_input_nodes_in_order = [] # FIX: 11/3/21 : # ADD CHECK IN _parse_state_feature_specs THAT IF A NODE RATHER THAN InputPort IS SPECIFIED, @@ -1644,8 +1733,7 @@ def _instantiate_input_ports(self, context=None): else: # Implement any specified state_features - state_input_ports_specs = self._parse_state_feature_specs(self.state_feature_specs, - self.state_feature_functions) + state_input_ports_specs = self._parse_state_feature_specs() # Note: # if state_features were specified and agent_rep is a CompositionFunctionApproximator, # assume they are OK (no way to check their validity for agent_rep.evaluate() method, and skip assignment @@ -1736,34 +1824,38 @@ def _validate_input_nodes(self, nodes, enforce=None): items_str = f"contains items ({items}) that are" message = f"The '{STATE_FEATURES}' specified for '{self.name}' {items_str} not in its {AGENT_REP} " \ f"('{self.agent_rep.name}'). Executing '{self.agent_rep.name}' before " \ - f"{'they' if singular else 'it'} are added will generate an error ." + f"{'they are' if singular else 'it is'} added will generate an error ." if enforce: raise OptimizationControlMechanismError(message) else: warnings.warn(message) - def _parse_state_feature_specs(self, state_feature_specs, feature_functions, context=None): + # FIX: 1/29/22 - REFACTOR TO SUPPORT TUPLE AND InportPort SPECIFICATIONI DICT FOR MULT. PROJS. TO STATE_INPUT_PORT + def _parse_state_feature_specs(self, context=None): """Parse entries of state_features specifications into InputPort spec dictionaries. Called from _instantiate_input_ports() state_features specify sources of values assigned to state_feature_values, and passed to agent_rep.evaluate() - as the inputs to its INPUT Nodes. - Each is used to create Projection(s) from specified source (may be direct or indirect from a nested Composition) - If the number of state_features specified is less than the number of agent_rep INPUT Nodes, - do not construct a state_input_port (as a result, no input is provided to the corresponding INPUT Node - when agent_rep.evaluate() executes, and the value of that NODE from its last execution is used. + as the inputs to its INPUT Nodes (**predicted_inputs argument if agent_rep is a Composition; + **feature_values** argument if it is a CompositionFunctionApproximator. + Use each to create Projection(s) from specified source; these may be direct, or indirect by way of a CIM + if the source is in a nested Composition). + If the number of state_features specified is less than the number of agent_rep INPUT Nodes, only construct + state_input_ports for the INPUT Nodes specified (for a list spec, the first n INPUT Nodes listed in + agent_rep.nodes, where n is the length of the list spec); as a result, the remaining INPUT Nodes are + provided their default variables as input when agent_rep.evaluate() executes. If shadowing is specified, set INTERNAL_ONLY to True in entry of params dict for state_input_port's InputPort specification dictionary (so that inputs to Composition are not required if the specified state_feature is for an INPUT Node). If an INPUT Node is specified that is not (yet) in agent_rep, and/or a source is specified that is not yet - in the self.composition, warn and defer creating a state_input_port; final check is made (and error + in the self.composition, warn and defer creating a state_input_port; final check is made, and error(s) generated for unresolved specifications at run time. Handle four formats: - list: list of sources; must be listed in same order as INPUT Nodes of agent_rep to which they correspond; - dict: keys are INPUT Nodes of agent_rep and values are corresponding sources; - - set: INPUT Nodes for which shadowing is specified + - set: INPUT Nodes for which shadowing is implemented - SHADOW_INPUTS dict: single entry with key='SHADOW_INPUTS" and values=list of sources (see above). Assign functions specified in **state_feature_functions** to InputPorts for all state_features @@ -1774,14 +1866,14 @@ def _parse_state_feature_specs(self, state_feature_specs, feature_functions, con from psyneulink.core.compositions.composition import Composition, NodeRole # Agent rep's input Nodes and their names agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) + # List of INPUT Nodes for which state_features are specified, ordered according to agent_rep.nodes + self._specified_input_nodes_in_order = [] + # List of parsed state_feature_specs + self._state_feature_specs_parsed = [] # VALIDATION AND WARNINGS ----------------------------------------------------------------------------------- - assert state_feature_specs == self.state_feature_specs, \ - f"PROGRAM ERROR: self.state_feature_specs for {self.name} not passed to _parse_state_feature_specs()." - # MODIFIED 1/26/22 NEW: FIX: MODIFY ONCE self.state_feature_specs is a Parameter(structural=True) - state_feature_specs = state_feature_specs.copy() - # MODIFIED 1/26/22 END + state_feature_specs = self.state_feature_specs # Only list spec allowed if agent_rep is a CompositionFunctionApproximator if self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR and not isinstance(state_feature_specs, list): @@ -1829,27 +1921,20 @@ def expand_nested_input_comp_to_input_nodes(comp): input_nodes.append(node) return input_nodes - def get_nodes_in_agent_rep_order(): - # FIX: 1/26/22 - MAKES ASSUMPTION THAT NODES NOT YET IN agent_rep WILL BE ADDED IN ORDER LISTED - # Re-order nodes according to their order in agent_rep.nodes, and append any not (yet) in agent_rep at end - # (assumes that ones not listed in order will be added to agent_rep later in the order listed) - node_specs = list(state_feature_specs) - nodes = [] - for node in agent_rep_input_nodes: - if node in node_specs: - nodes.append(node_specs.pop(node_specs.index(node))) - nodes.extend(node_specs) - return nodes - def get_inputs_for_nested_comp(comp): # FIX: 1/18/22 - NEEDS TO BE MODIFIED TO RETURN TUPLE IF > INPUT NODE, ONCE THAT CAN BE HANDLED BY LIST SPEC return comp.get_nodes_by_role(NodeRole.INPUT) - # List of INPUT Nodes ordered for use elswewhere (e.g., self.state_features) - self._specified_input_nodes_in_order = [] - source_specs_for_input_nodes = [] + # PARSE SPECS ------------------------------------------------------------------------------------------ + # Generate parallel lists of INPUT Nodes and corresponding feature specs (for sources of inputs) + + def _parse_specs(state_feature_specs, spec_str="list"): + """Validate and parse specs into Port references and construct state_features dict + Validate number and identity of specs relative to agent_rep INPUT Nodes. + Assign {node: spec} entries to state_features dict + Return names for use as input_port_names in main body of method + """ - def instantiate_list_spec(state_feature_specs, spec_str="list"): if self.agent_rep_type == COMPOSITION: if len(state_feature_specs) > len(agent_rep_input_nodes): nodes_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" @@ -1880,103 +1965,132 @@ def instantiate_list_spec(state_feature_specs, spec_str="list"): f"({comp_names}) in the {spec_str} specified for its '{STATE_FEATURES}' argument; these must be " f"replaced by direct references to the Mechanisms (or their InputPorts) within them to be " f"shadowed.") - # Get INPUT Nodes for items in list (assuming items are in order of INPUT Nodes of agent_rep) - nodes = [] - specs = [] spec_names = [] - for i, spec in enumerate(state_feature_specs): - if spec is None: - continue - # Assign spec - specs.append(spec) + num_specs = len(state_feature_specs) + num_nodes = len(agent_rep_input_nodes) + self._num_state_feature_specs = max(num_specs, num_nodes) + for i in range(self._num_state_feature_specs): + # NODE & NODE_NAME + spec_name = None + if i < num_nodes: + # # MODIFIED 1/29/22 OLD: + # # Node should be in agent_rep, so use that to be sure + # node = agent_rep_input_nodes[i] + # node_name = node.name + # MODIFIED 1/29/22 NEW: + # FIX: 1/29/22 - NEED TO HANDLE agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR HERE + # Node should be in agent_rep, so use that to be sure + if self.agent_rep_type == COMPOSITION: + node = agent_rep_input_nodes[i] + else: + spec = state_feature_specs[i] + node = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner + node_name = node.name + # MODIFIED 1/29/22 END + else: + # Node not (yet) in agent_rep, so uses its node name + spec = state_feature_specs[i] + node = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner + node_name = node.name + # SPEC + # Assign specs # Only process specs for which there are already INPUT Nodes in agent_rep # (others may be added to Composition later) - if i < len(agent_rep_input_nodes): - # Assign node - node = agent_rep_input_nodes[i] - nodes.append(node) + if i < num_specs: + spec = state_feature_specs[i] # Assign input_port name if is_numeric(spec): - spec_names.append(f"{node.name} {DEFAULT_VARIABLE.upper()}") - else: + spec_name = f"{node_name} {DEFAULT_VARIABLE.upper()}" + elif isinstance(spec, (Port, Mechanism, Composition)): if hasattr(spec, 'full_name'): - spec_names.append(spec.full_name) + spec_name = spec.full_name else: - spec_names.append(spec.name) - - return nodes or None, specs, spec_names or [] - - # PARSE SPECS ------------------------------------------------------------------------------------------ - # Generate parallel lists of INPUT Nodes and corresponding feature specs (for sources of inputs) + spec_name = spec.name + else: + # Fewer specifications than number of INPUT Nodes, + # the remaining ones may be specified later, but for now assume they are meant to be ignored + spec = None + self._specified_input_nodes_in_order.append(node) + self._state_feature_specs_parsed.append(spec) + spec_names.append(spec_name) + return spec_names or [] # LIST spec # Treat as source specs: # - construct a regular dict using INPUT Nodes as keys and specs as values if isinstance(state_feature_specs, list): - nodes, specs, names = instantiate_list_spec(state_feature_specs) - state_feature_specs = specs - self._specified_input_nodes_in_order = nodes - input_port_names = names + input_port_names = _parse_specs(state_feature_specs) # DICT spec elif isinstance(state_feature_specs, dict): # SHADOW_INPUTS dict spec if SHADOW_INPUTS in state_feature_specs: + # Set not allowed as SHADOW_INPUTS spec if isinstance(state_feature_specs[SHADOW_INPUTS], set): # Catch here to provide context-relevant error message raise OptimizationControlMechanismError( f"The '{STATE_FEATURES}' argument for '{self.name}' uses a set in a '{SHADOW_INPUTS.upper()}' " f"dict; this must be a single item or list of specifications in the order of the INPUT Nodes" f"of its '{AGENT_REP}' ({self.agent_rep.name}) to which they correspond." ) - nodes, specs, names = instantiate_list_spec(state_feature_specs[SHADOW_INPUTS], + input_port_names = _parse_specs(state_feature_specs[SHADOW_INPUTS], f"{SHADOW_INPUTS.upper()} dict") - state_feature_specs[SHADOW_INPUTS] = specs - self._specified_input_nodes_in_order = nodes - input_port_names = names # User {node:spec} dict spec else: - state_feature_specs = {k:v for k,v in state_feature_specs.items() if v is not None} - # Get INPUT nodes in order listed in agent_rep.nodes - self._specified_input_nodes_in_order = get_nodes_in_agent_rep_order() - self._validate_input_nodes(state_feature_specs.keys()) - # Get specs in the same order: - specs = [state_feature_specs[node] for node in self._specified_input_nodes_in_order] + specified_input_nodes = state_feature_specs.keys() + self._validate_input_nodes(specified_input_nodes) + nodes = self._get_agent_rep_input_nodes(comp_as_node=True) + # Get specs in order of agent_rep INPUT Nodes, with None assigned to any unspecified INPUT Nodes + # as well as to any not in agent_rep at end which are placed at the end of the list + nodes.extend([node for node in specified_input_nodes if node not in nodes]) + specs = [state_feature_specs[node] if node in specified_input_nodes else None for node in nodes] # Get parsed specs and names (don't care about nodes since those are specified by keys - _ , state_feature_specs, input_port_names = instantiate_list_spec(specs) + input_port_names = _parse_specs(specs) # SET spec # Treat as specification of INPUT Nodes to be shadowed: # - construct an InputPort dict with SHADOW_INPUTS as its key, and specs in a list as its value elif isinstance(state_feature_specs, set): # All nodes must be INPUT nodes of agent_rep, that are to be shadowed, - # Order the set and place in list - self._specified_input_nodes_in_order = get_nodes_in_agent_rep_order() self._validate_input_nodes(state_feature_specs) + # specs = [node if node in state_feature_specs else None for node in agent_rep_input_nodes] + # FIX: 1/29/22 - + # THIS IS DANGEROUS -- SHOULD REPLACE ONCE TUPLE FORMAT IS IMPLEMENTED OR USE INPUTPORT SPECIF DICT + # IT WORKS FOR NESTED COMPS WITH A SIGNLE INPUT NODE OF THEIR OWN + # BUT IF A NESTED COMP HAS MORE THAN ONE INPUT NODE, THIS WILL RETURN MORE THAN ONE NODE IN PLACE OF + # THE NESTED COMP AND THUS GET OUT OF ALIGNMENT WITH NUMBER OF INPUT NODES FOR AGENT_REP + # ONCE FIXED, EXTEND FOR USE WITH COMP AS SPEC IN LIST AND DICT FORMATS # Replace any nested Comps that are INPUT Nodes of agent_comp with their INPUT Nodes so they are shadowed all_nested_input_nodes = [] - for node in self._specified_input_nodes_in_order: + for node in state_feature_specs: if isinstance(node, Composition): all_nested_input_nodes.extend(get_inputs_for_nested_comp(node)) else: all_nested_input_nodes.append(node) - # so reformat as SHADOW_INPUTS dict handled by _parse_shadow_inputs() below - source_specs_for_input_nodes = all_nested_input_nodes - state_feature_specs = {SHADOW_INPUTS:source_specs_for_input_nodes} + # Get specs in order of agent_rep INPUT Nodes, with any not in agent_rep at end + # NEED TO ADD Nones TO LIST IF NOT SPECIFIED + agent_rep_nodes = self._get_agent_rep_input_nodes() + nodes = [node if node in all_nested_input_nodes else None for node in agent_rep_nodes] + nodes.extend([node for node in all_nested_input_nodes if node not in agent_rep_nodes]) + input_port_names = _parse_specs(nodes) # CONSTRUCT InputPort SPECS ----------------------------------------------------------------------------- - _state_input_ports = _parse_shadow_inputs(self, state_feature_specs) - - parsed_features = [] - - for i, spec in enumerate(_state_input_ports): + state_input_port_specs = [] + for i in range(self._num_state_feature_specs): + spec = self._state_feature_specs_parsed[i] + # node = self._specified_input_nodes_in_order[i] + if spec is None: + continue + spec = _parse_shadow_inputs(self, spec) # If spec is numeric, assign as default value and InputPort function that simply returns that value if is_numeric(spec): spec_val = copy.copy(spec) spec = {VALUE: spec_val, PARAMS: {DEFAULT_INPUT: DEFAULT_VARIABLE} } + else: + spec = spec[0] # If optimization uses Composition, assume that shadowing a Mechanism means shadowing its primary InputPort if isinstance(spec, Mechanism): if self.agent_rep_type == COMPOSITION: @@ -1990,15 +2104,12 @@ def instantiate_list_spec(state_feature_specs, spec_str="list"): spec = spec.input_port else: spec = spec.output_port + # Update Mechanism spec with Port + self._state_feature_specs_parsed[i] = spec parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) if not parsed_spec[NAME]: - if i < len(input_port_names): - # Use keys from input dict as names of state_input_ports - # (needed by comp._build_predicted_inputs_dict to identify INPUT nodes) - parsed_spec[NAME] = input_port_names[i] - else: - parsed_spec[NAME] = spec.full_name if isinstance(spec, Port) else spec.name + parsed_spec[NAME] = input_port_names[i] if parsed_spec[PARAMS] and SHADOW_INPUTS in parsed_spec[PARAMS]: # Composition._update_shadow_projections will take care of PROJECTIONS specification @@ -2007,21 +2118,18 @@ def instantiate_list_spec(state_feature_specs, spec_str="list"): # GET FEATURE FUNCTIONS ----------------------------------------------------------------------------- - if feature_functions: - if isinstance(feature_functions, dict) and spec in feature_functions: + if self.state_feature_functions: + if isinstance(self.state_feature_functions, dict) and spec in self.state_feature_functions: + feature_functions = self.feature_functions.copy() feat_fct = feature_functions.pop(spec) else: - feat_fct = feature_functions + feat_fct = self.state_feature_functions parsed_spec.update({FUNCTION: self._parse_state_feature_function(feat_fct)}) parsed_spec = [parsed_spec] # so that extend works below - parsed_features.extend(parsed_spec) + state_input_port_specs.extend(parsed_spec) - # # # MODIFIED 1/24/22 OLD: - # # FIX - 1/25/22: REINSTATE ONCE self.state_feature_specs IS A PARAMETER WITH structural=True - # self.state_feature_specs = state_feature_specs - # MODIFIED 1/24/22 END - return parsed_features + return state_input_port_specs def _parse_state_feature_function(self, feature_function): if isinstance(feature_function, Function): @@ -2029,6 +2137,23 @@ def _parse_state_feature_function(self, feature_function): else: return feature_function + def _update_state_features_dict(self): + # FIX: 1/30/22 - ??REFACTOR TO USE Composition aux_components?? + # OR IMPLEMENT LIST WITH DEFERRED ITEMS STORED THERE (THAT WAY DON'T HAVE TO RELY ON NAME BELOW) + # OR IMPLEMENT INTERNAL _state_features dict THEN state_features AS A PROPERTY + # THAT TRACKS state_input_ports AS BEFORE + # FIX: MODIFY THIS TO INDICATE WHICH SPECS ARE STILL MISSING + # FIX: HANDLE ERRORS HERE INSTEAD OF _validate_state_features OR EXECUTE THAT FIRST AND CAPTURE HERE + for i, port in enumerate(self.state_input_ports): + node = self._specified_input_nodes_in_order[i] + feature = self._state_feature_specs_parsed[i] + if not (isinstance(node, str) and 'DEFERRED' in node): + continue + if feature.owner not in self._get_agent_rep_input_nodes(): + # assert False, f"PROGRAM ERROR: Node not in {self.agent_rep.name} should have been caught above." + continue + self._state_feature_specs_parsed[i] = feature + def _update_state_input_ports_for_controller(self, context=None): """Check and update state_input_ports for model-based optimization (agent_rep==Composition) @@ -2069,8 +2194,17 @@ def _update_state_input_ports_for_controller(self, context=None): # Restrict validation and any further instantiation of state_input_ports # until run time, when the Composition is expected to be fully constructed if context._execution_phase == ContextFlags.PREPARING: + # MODIFIED 1/30/22 NEW: + # FIX: 1/30/22 - NEEDS TO EXECUTE ON UPDATES WITHOUT RUN, + # BUT MANAGE ERRORS WRT TO _validate_state_features + self._update_state_features_dict() + # MODIFIED 1/30/22 END self._validate_state_features() + # MODIFIED 1/30/22 OLD: return + # # MODIFIED 1/30/22 NEW: + # return True + # MODIFIED 1/30/22 END elif not self.state_input_ports: # agent_rep is Composition, but no state_features have been specified, @@ -2087,10 +2221,6 @@ def _update_state_input_ports_for_controller(self, context=None): # input_port = input_port. shadow_input_ports.append(input_port) - # Get list of nodes with any nested Comps that are INPUT Nodes listed as the node - # (this is what is used in the state_features dict and state_features property) - self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) - local_context = Context(source=ContextFlags.METHOD) state_input_ports_to_add = [] # for input_port in input_ports_not_specified: @@ -2113,6 +2243,8 @@ def _update_state_input_ports_for_controller(self, context=None): context=local_context) self.state_input_ports.extend(state_input_ports_to_add) + self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) + self._state_feature_specs_parsed = [input_port.shadow_inputs for input_port in self.state_input_ports] return True def _validate_state_features(self): @@ -2276,22 +2408,6 @@ def _instantiate_control_signals(self, context): Assign RANDOMIZATION_CONTROL_SIGNAL for random_variables """ - # MODIFIED 11/21/21 NEW: - # FIX - PURPOSE OF THE FOLLOWING IS TO "CAPTURE" CONTROL SPECS MADE LOCALLY ON MECHANISMS IN THE COMP - # AND INSTANTIATE ControlSignals FOR THEM HERE, ALONG WITH THOSE SPECIFIED IN THE CONSTRUCTOR - # FOR THE OCM. ALSO CAPTURES DUPLICATES (SEE MOD BELOW). - # FIX: WITHOUT THIS, GET THE mod param ERROR; WITH IT, GET FAILURES IN test_control: - # TestModelBasedOptimizationControlMechanisms_Execution - # test_evc - # test_stateful_mechanism_in_simulation - # TestControlMechanisms: - # test_lvoc - # test_lvoc_both_prediction_specs - # test_lvoc_features_function - # if self.agent_rep and self.agent_rep.componentCategory=='Composition': - # control_signals_from_composition = self.agent_rep._get_control_signals_for_composition() - # self.output_ports.extend(control_signals_from_composition) - # MODIFIED 11/21/21 END control_signals = [] for i, spec in list(enumerate(self.output_ports)): control_signal = self._instantiate_control_signal(spec, context=context) @@ -2424,12 +2540,6 @@ def _execute(self, variable=None, context=None, runtime_params=None): if self.is_initializing: return [defaultControlAllocation] - # # FIX: THESE NEED TO BE FOR THE PREVIOUS TRIAL; ARE THEY FOR FUNCTION_APPROXIMATOR? - # FIX: NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED - self.parameters.state_feature_values._set(_parse_state_feature_values_from_variable( - self.num_outcome_input_ports, - variable), context) - # Assign default control_allocation if it is not yet specified (presumably first trial) control_allocation = self.parameters.control_allocation._get(context) if control_allocation is None: @@ -2442,8 +2552,7 @@ def _execute(self, variable=None, context=None, runtime_params=None): # have an adapt method, we also don't need to call the net_outcome getter net_outcome = self.parameters.net_outcome._get(context) - # FIX: NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED - self.agent_rep.adapt(_parse_state_feature_values_from_variable(self.num_outcome_input_ports, variable), + self.agent_rep.adapt(self.parameters.state_feature_values._get(context), control_allocation, net_outcome, context=context) @@ -2936,51 +3045,54 @@ def num_state_input_ports(self): @property def state_features(self): - """Return dict with {INPUT Node: source} entries for specifications in **state_features** arg of constructor.""" - - num_instantiated_state_features = len([p for p in self.state_input_ports - if p.path_afferents or p.default_input]) - input_nodes = None - state_features = None - if isinstance(self.state_feature_specs, dict): - if SHADOW_INPUTS in self.state_feature_specs: - # FIX: 1/26/22 - NEED TO DEAL WITH None - # input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) - state_features = self.state_feature_specs[SHADOW_INPUTS] - input_nodes = [node for node, spec in zip(self._get_agent_rep_input_nodes(comp_as_node=True), - state_features) if spec is not None] - else: - # FIX: NOT SURE THIS IS SAFE; ASSUMES ITEMS ARE INSTANTIATED IN ORDER LISTED IN Composition.nodes - input_nodes = self._specified_input_nodes_in_order[:num_instantiated_state_features] + # FIX: 1/30/22 - REFACTOR TO USE _state_feature_specs_parsed and _specified_input_nodes_in_order + agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) + state_features_dict = {(k if k in agent_rep_input_nodes else f"{k.name} DEFERRED"):v + for k,v in zip(self._specified_input_nodes_in_order, + self._state_feature_specs_parsed)} + return state_features_dict + + @property + def state_feature_sources(self): + """Dict with {INPUT Node: source} entries for sources specified in **state_features** arg of constructor.""" + # FIX: 1/30/22: + # FIX: SUPPORT tuple SPECIFICATION FOR sources + # FIX: REDUCE ALL MECHANISM SOURCES TO Port SPECS + # FIX: ??REFACTOR TO CONSTRUCT THE FOLLOWING: + # state_feature_specs_dict: {INPUT Node: spec} + # state_features: {INPUT Node: source} + if isinstance(self.state_feature_specs, dict) and SHADOW_INPUTS in self.state_feature_specs: + # SHADOW_INPUTS dict + state_feature_specs = self.state_feature_specs[SHADOW_INPUTS] else: - # List spec - state_features = self.state_feature_specs - if not self.state_feature_specs: - # Automatic assignment - input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) - elif not input_nodes: - # List spec - input_nodes = [node for node, spec in zip(self._get_agent_rep_input_nodes(comp_as_node=True), - state_features) if spec is not None] + # list and set specs + state_feature_specs = self.state_feature_specs sources = [source_tuple[0] if source_tuple[0] != DEFAULT_VARIABLE else value - for source_tuple,value in list(self.state_dict.items())[:num_instantiated_state_features]] - return {k:v for k,v in zip(input_nodes, sources)} + for source_tuple, value in self.state_distal_sources_and_destinations_dict.items()] + return {k:v for k,v in zip(self.state_features, sources)} + + def _state(self, context=None): + """Get context-specific state_feature and control_allocation values""" + # Use self.state_feature_values Parameter if state_features specified; else use state_input_port values + state_feat_vals = self.parameters.state_feature_values.get(context) + state_feature_values = state_feat_vals if len(state_feat_vals) else self.state_input_ports.values + # FIX: 1/30/22: USE CONTEXT TO GET control_allocations IF THAT IS NOT ALREADY DONE ON THAT ATTRIBUTE + return [v.tolist() for v in state_feature_values] + self.control_allocation.tolist() @property def state(self): - state_feature_values = (self.state_feature_values if len(self.state_feature_values) - else self.state_input_ports.values) - # return np.append(state_feature_values, self.control_allocation, 0) - return [v.tolist() for v in state_feature_values] + self.control_allocation.tolist() + """Array that is concatenation of state_feature_values and control_allocations""" + # Use self.state_feature_values Parameter if state_features specified; else use state_input_port values + return self.state_feature_values + self.control_allocation.tolist() @property - def state_dict(self): + def state_distal_sources_and_destinations_dict(self): """Return dict with (Port, Node, Composition, index) tuples as keys and corresponding state[index] as values. Initial entries are for sources of the state_feature_values (i.e., distal afferents for state_input_ports) - and subsequent entries are for parameters modulated by the OptimizationControlMechanism's ControlSignals - (i.e., distal destinations of its ControlProjections). + and subsequent entries are for destination parameters modulated by the OptimizationControlMechanism's + ControlSignals (i.e., distal efferents of its ControlProjections). Note: the index is required, since a state_input_port may have more than one afferent Projection - (that is, a state_feature_value may be determined by Projections from more than one Node), + (that is, a state_feature_value may be determined by Projections from more than one source), and a ControlSignal may have more than one ControlProjection (that is, a given element of the control_allocation may apply to more than one Parameter). However, for state_input_ports that shadow a Node[InputPort], only that Node[InputPort] is listed in state_dict even if the Node[InputPort] being @@ -2991,6 +3103,9 @@ def state_dict(self): state_dict = {} + # FIX: 1/30/22 - RECONCILE AND DOCUMENT MULTIPLE POSSIBLE SOURCES/DESTINATIONS FOR + # - THIS DICT AND STATE_FEATURES DICTS, WHICH MAY HAVE MORE THAN ONE ENTRY PER INPUT_NODE + # - VS. STATE_FEATURE_VALUES AND STATE_FEATURE_SPECS_DICT (WHICH HAVE ONLY ONE ENTRY PER INPUT_NODE # Get sources for state_feature_values of state: for state_index, port in enumerate(self.state_input_ports): if not port.path_afferents: diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 71952dca1a0..8611fda7ee4 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -3916,6 +3916,12 @@ def add_node(self, node, required_roles=None, context=None): self._need_check_for_unused_projections = True + # # MODIFIED 1/27/22 NEW: FIX - BREAKS test_learning_output_shape() in ExecuteMode.LLVM + # if context.source != ContextFlags.METHOD: + # # Call _analyze_graph with ContextFlags.METHOD to avoid recursion + # self._analyze_graph(context=Context(source=ContextFlags.METHOD)) + # MODIFIED 1/27/22 END + def add_nodes(self, nodes, required_roles=None, context=None): """ Add a list of `Nodes ` to the Composition. @@ -7456,7 +7462,7 @@ def _create_non_terminal_backprop_learning_components(self, receiver=learning_mechanism.error_signal_input_ports[i]) error_projections.append(error_projection) - self.add_node(learning_mechanism, required_roles=NodeRole.LEARNING) + self.add_node(learning_mechanism, required_roles=NodeRole.LEARNING, context=context) try: act_in_projection = MappingProjection(sender=input_source.output_ports[0], receiver=learning_mechanism.input_ports[0]) @@ -8027,6 +8033,7 @@ def _check_nodes_initialization_status(self, context=None): # FIX: 11/3/21 ??GET RID OF THIS AND CALL TO IT ONCE PROJECTIONS HAVE BEEN IMPLEMENTED FOR SHADOWED INPUTS # CHECK WHETHER state_input_ports ADD TO OR REPLACE shadowed_inputs + # FIX: 1/28/22 - NEED TO ACCOMODATE None OR MISSING state_feature_values, EITHER HERE OR IN predicted_inputs def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): """Format predict_inputs from controller as input to evaluate method used to execute simulations of Composition. @@ -8035,9 +8042,14 @@ def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): Deal with inputs for nodes in nested Compositions """ controller = controller or self.controller + # FIX: 1/29/22 - REFACTOR TO USE OCM.state_features DICT? # Use keys for inputs dict from OptimizationControlMechanism state_features if it is specified as a dict # (unless it has SHADOW_INPUTS entry, in which case that is handled below) - input_dict_keys = controller.agent_rep.get_nodes_by_role(NodeRole.INPUT)[:len(controller.state_input_ports)] + # # MODIFIED 1/29/22 OLD: + # input_dict_keys = controller.agent_rep.get_nodes_by_role(NodeRole.INPUT)[:len(controller.state_input_ports)] + # MODIFIED 1/29/22 NEW: + input_dict_keys = list(controller.state_features.keys()) + # MODIFIED 1/29/22 END inputs = {} no_predicted_input = (predicted_inputs is None or not len(predicted_inputs)) diff --git a/psyneulink/core/globals/parameters.py b/psyneulink/core/globals/parameters.py index 27ffe9efcf0..424c7f95e8b 100644 --- a/psyneulink/core/globals/parameters.py +++ b/psyneulink/core/globals/parameters.py @@ -30,6 +30,10 @@ ``t.defaults.noise`` is shorthand for ``t.parameters.noise.default_value``, and they both refer to the default ``noise`` value for *t* +Default values are sometimes also used when the parameters value has not been specified; for example, a Component's +``defaults.variable`` is used as the input to a `Mechanism` if its `execute ` method is called +without any input specified, and similarly it is used for the `INPUT ` `Nodes ` of +a `Composition` which are not specified in the **inputs** argument of its `run ` method. .. _Parameter_Statefulness: diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 3892d2526af..c3c50fc4bad 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -314,7 +314,8 @@ def test_partial_deferred_init(self, state_features_option): deferred_node_control_signal ]) ) - assert ocomp.controller.state_features == {initial_node_a: initial_node_a.input_port} + assert ocomp.controller.state_features == {initial_node_a: initial_node_a.input_port, + 'deferred DEFERRED':deferred_node.input_port} if state_features_option in {'list', 'shadow_inputs_dict'}: # expected_text = 'The number of \'state_features\' specified for Controller (2) is more than the ' \ @@ -849,6 +850,8 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ('list_spec_with_none', None, None, None), ('input_dict_spec', None, None, None), ('input_dict_spec_short', None, None, None), + ('set_spec', None, None, None), + ('set_spec_short', None, None, None), ('automatic_assignment', None, None, None), ('shadow_inputs_dict_spec', None, None, None), ('shadow_inputs_dict_spec_w_none', None, None, None), @@ -870,6 +873,12 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c @pytest.mark.control @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x[0] for x in state_feature_args]) def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_args): + + test_condition = state_feature_args[0] + message_1 = state_feature_args[1] + message_2 = state_feature_args[2] + exception_type = state_feature_args[3] + ia = pnl.ProcessingMechanism(name='IA') ib = pnl.ProcessingMechanism(name='IB') ic = pnl.ProcessingMechanism(name='IC') @@ -881,15 +890,17 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ocomp = pnl.Composition(pathways=[icomp], name='OUTER COMP') ocomp.add_linear_processing_pathway([oa,oc]) ocomp.add_linear_processing_pathway([ob,oc]) + state_features_dict = { # Legal state_features specifications 'partial_legal_list_spec': [oa.output_port], 'full_list_spec': [ia.input_port, oa.output_port, [3,1,2]], 'list_spec_with_none': [ia.input_port, None, [3,1,2]], 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK - 'input_dict_spec_short': {oa:oc.input_port, ob:ob.output_port}, # Note: missing oa spec + 'input_dict_spec_short': {ob:ob.output_port, oa:oc.input_port}, # Note: missing oa spec and out of order # 'input_dict_spec': {oa:oc.input_port, ia:ia, ob:ob.output_port}, # <- ia is in nested Comp doesn't work - 'set_spec': {ob, icomp, oa}, # Note: out of order is OK + 'set_spec': {ob, icomp, oa}, # Note: out of order is OK, and use of Nested comp as spec + 'set_spec_short': {oa}, 'automatic_assignment': None, 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, 'shadow_inputs_dict_spec_w_none': {pnl.SHADOW_INPUTS:[ia, None, ob]}, @@ -908,12 +919,10 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'bad_dict_spec_error': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node 'bad_set_spec_warning': {ob, ia}, # elicits both short spec and not INPUT Node warnings (for both ob and ia) 'bad_set_spec_error': {ob, ia}, # elicits both short spec and not INPUT Node warnings (for both ob and ia) - 'comp_in_list_spec':[icomp, oa.output_port, [3,1,2]], + 'comp_in_list_spec':[icomp, oa.output_port, [3,1,2]], # FIX: REMOVE ONCE TUPLE FORMAT SUPPORTED 'comp_in_shadow_inupts_spec':{pnl.SHADOW_INPUTS:[icomp, oa, ob]} } - state_features = state_features_dict[state_feature_args[0]] - message_1 = state_feature_args[1] - message_2 = state_feature_args[2] + state_features = state_features_dict[test_condition] ocm = pnl.OptimizationControlMechanism(state_features=state_features, objective_mechanism=[ic,ib], function=pnl.GridSearch(), @@ -921,90 +930,116 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg allocation_samples=[10, 20, 30]), pnl.ControlSignal(modulates=(pnl.INTERCEPT,oc), allocation_samples=[10, 20, 30])]) - if not state_feature_args[1]: + if not exception_type: ocomp.add_controller(ocm) ocomp.run() - if state_feature_args[0] == 'full_list_spec': + if test_condition == 'full_list_spec': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'OA[OutputPort-0]', 'OB DEFAULT_VARIABLE'] assert ocm.state_features == {icomp: ia.input_port, oa: oa.output_port, ob: [3, 1, 2]} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [3, 1, 2]])) + - if state_feature_args[0] == 'list_spec_with_none': + if test_condition == 'list_spec_with_none': assert len(ocm.state_input_ports) == 2 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'OB DEFAULT_VARIABLE'] - assert ocm.state_features == {icomp: ia.input_port, ob: [3, 1, 2]} + assert ocm.state_features == {icomp: ia.input_port, oa: None, ob: [3, 1, 2]} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [3, 1, 2]])) + - elif state_feature_args[0] == 'input_dict_spec': + elif test_condition == 'input_dict_spec': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OC[InputPort-0]', 'OB[OutputPort-0]'] # 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK assert ocm.state_features == {icomp:ia.input_port, oa:oc.input_port, ob:ob.output_port} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) - elif state_feature_args[0] == 'input_dict_spec_short': + elif test_condition == 'input_dict_spec_short': assert len(ocm.state_input_ports) == 2 assert ocm.state_input_ports.names == ['Shadowed input of OC[InputPort-0]', 'OB[OutputPort-0]'] - assert ocm.state_features == {oa:oc.input_port, ob:ob.output_port} + assert ocm.state_features == {icomp: None, oa:oc.input_port, ob:ob.output_port} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) - elif state_feature_args[0] == 'set_spec': + elif test_condition == 'set_spec': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - # 'set_spec': {ob, icomp, oa}, # Note: out of order is OK assert ocm.state_features == {icomp:ia.input_port, oa:oa.input_port, ob:ob.input_port} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) + + elif test_condition == 'set_spec_short': + assert len(ocm.state_input_ports) == 1 + assert ocm.state_input_ports.names == ['Shadowed input of OA[InputPort-0]'] + # 'set_spec': {ob, icomp, oa}, # Note: out of order is OK + assert ocm.state_features == {icomp:None, oa:oa.input_port, ob:None} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) - elif state_feature_args[0] == 'automatic_assignment': + elif test_condition == 'automatic_assignment': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] assert ocm.state_features == {icomp: ia.input_port, oa: oa.input_port, ob: ob.input_port} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) - elif state_feature_args[0] == 'shadow_inputs_dict_spec': + elif test_condition == 'shadow_inputs_dict_spec': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] assert ocm.state_features == {icomp: ia.input_port, oa: oa.input_port, ob: ob.input_port} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) - elif state_feature_args[0] == 'shadow_inputs_dict_spec_w_none': + elif test_condition == 'shadow_inputs_dict_spec_w_none': assert len(ocm.state_input_ports) == 2 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {icomp: ia.input_port, ob: ob.input_port} + assert ocm.state_features == {icomp: ia.input_port, oa: None, ob: ob.input_port} + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) - elif state_feature_args[3] is UserWarning: + elif exception_type is UserWarning: # These also produce errors, tested below - if state_feature_args[0] in {'too_many_inputs_warning', - 'too_many_w_node_not_in_composition_warning', - 'bad_dict_spec_warning', - 'bad_set_spec_warning'}: + if test_condition in {'too_many_inputs_warning', + 'too_many_w_node_not_in_composition_warning', + 'bad_dict_spec_warning', + 'bad_set_spec_warning'}: with pytest.warns(UserWarning) as warning: ocomp.add_controller(ocm) assert warning[0].message.args[0] == message_1 - if state_feature_args[0] in 'bad_set_spec_warning': + if test_condition == 'bad_set_spec_warning': assert message_2 == warning[1].message.args[0] # since set, order of ob and ia is not reliable else: with pytest.warns(UserWarning) as warning: ocomp.add_controller(ocm) ocomp.run() - if state_feature_args[0] == 'partial_legal_list_spec': + if test_condition == 'partial_legal_list_spec': assert len(ocm.state_input_ports) == 1 assert ocm.state_input_ports.names == ['OA[OutputPort-0]'] - assert ocm.state_features == {icomp: oa.output_port} # Note: oa is assigned to icomp due to ordering + # Note: oa is assigned to icomp due to ordering: + assert ocm.state_features == {icomp: oa.output_port, oa: None, ob: None} assert warning[0].message.args[0] == message_1 - assert ocm.state_features == {icomp: oa.output_port} + assert ocm.state_features == {icomp: oa.output_port, oa: None, ob: None} else: - with pytest.raises(state_feature_args[3]) as error: + with pytest.raises(exception_type) as error: ocomp.add_controller(ocm) ocomp.run() assert message_1 in str(error.value) @@ -1037,10 +1072,10 @@ def test_ocm_state_and_state_dict(self): ) ocomp.add_controller(ocm) assert all(np.allclose(x,y) for x,y in zip(ocm.state, [[0.0], [0.0], [3.0, 1.0, 2.0], [1.0], [1.0]])) - assert len(ocm.state_dict) == 6 - keys = list(ocm.state_dict.keys()) - values = list(ocm.state_dict.values()) - for key, value in ocm.state_dict.items(): + assert len(ocm.state_distal_sources_and_destinations_dict) == 6 + keys = list(ocm.state_distal_sources_and_destinations_dict.keys()) + values = list(ocm.state_distal_sources_and_destinations_dict.values()) + for key, value in ocm.state_distal_sources_and_destinations_dict.items(): ocm.state[key[3]] == value assert keys[0] == (ia.input_port, ia, icomp ,0) assert keys[1] == (oa.output_port, oa, ocomp, 1) @@ -1048,6 +1083,10 @@ def test_ocm_state_and_state_dict(self): assert keys[3] == (ia.parameter_ports[pnl.SLOPE], ia, icomp, 3) assert keys[4] == (oc.parameter_ports[pnl.INTERCEPT], oc, ocomp, 4) assert keys[5] == (oc.parameter_ports[pnl.SLOPE], oc, ocomp, 4) + ocomp.run() + assert all(np.allclose(expected,actual) + for expected,actual in zip(ocm.state_feature_values, + [[0.], [0.], [3, 1, 2]])) def test_modulation_of_control_signal_intensity_cost_function_MULTIPLICATIVE(self): # tests multiplicative modulation of default intensity_cost_function (Exponential) of From 1b23904d3751e98039bda69d995e1efcec78151e Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 2 Feb 2022 21:54:50 -0500 Subject: [PATCH 118/285] Feat/ocm/state features (#2302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - _update_state_input_ports_for_controller: - skip if ContextFlags.METHOD -> ContextFlags.PROCESSING * • optimizationcontrolmechanism.py: - add _validate_state_features - _update_state_input_ports_for_controller: validation of state_features moved to _validate_state_features * - * • optimizationcontrolmechanism.py - Parameters: add outcome_input_ports and state_input_ports * • controlmechanism.py & optimizationcontrolmechanism.py - move outcome_input_ports Parameter from ocm to controlmechanism - state_features: set to make Parameter on ocm, but waiting to deal with LLVM * • controlmechanism.py & optimizationcontrolmechanism.py - move outcome_input_ports Parameter from ocm to controlmechanism - state_features: set to make Parameter on ocm, but waiting to deal with LLVM * Passing all tests but ones in test_show_graph and predator_prey * • test_show_graph.py: - correct output for fix of typo • test_greedy_agent.py: - test_predator_prey(): reorder args in state_features to fix failure * - * - * - * • inputport.py: - _parse_self_port_type_spec(): change name of shadowed inputports to use full_name * - * - * - * - * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): - convert list spec to dict - handle nested Comp INPUT Node in set spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): passes all tests * - * - * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): errors for comp spec in list or SHADOW_INPUTS dict (should be replaced by expansion to INPUT Nodes of comp • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add tests for comp spec in list or SHADOW_INPUTS dict (replace with checks when expansion of these is handled) * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order) *** TBI state_features needs to be modified to accomodate this *** • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add test for None in list spec * • optimizationcontrolmechanism.py state_features: use self._input_nodes_for_state_features_specs * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order) *** TBI state_features needs to be modified to accomodate this *** • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add test for None in list spec * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(), _validate_state_features(): - support None in list format (ignores INPUT Node at specified point in order): fully implemented TBI: implement for SHADOW_INPUTS list spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): passes all tests * • optimizationcontrolmechanism.py: - move warning for specs < INPUT Nodes from _validate_state_features() to _parse_state_feature_specs() * • optimizationcontrolmechanism.py: - move warning for specs < INPUT Nodes from _validate_state_features() to _parse_state_feature_specs() * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): handle None in SHADOW_INPUTS spec • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors: - properly handle shadow_inputs_dict_spec and shadow_inputs_dict_spec_w_none conditions * Passes all test_ocm_state_feature_specs_and_warnings_and_errors tests, but uses internal dict that fails for deferred_init of agent_rep, since INPUT nodes (used as keys in dict) may not yet have been constructed. * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): refactored to use parallel lists rather than dict for nodes and specs • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - all tests pass * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): still needs to deal with CFA * - * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • optimizationcontrolmechanism.py _parse_state_feature_specs(): - suppress warning for more state_features than INPUT Nodes in agent_rep (duplicates exception raised in _validate_state_features -- but see comment) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): still fails warning on 'too_many_inputs_warning' * • composition.py - run(): add context.execution_phase = ContextFlags.PREPARING at beginning of method * - * - * MODS TO DEFER VALIDATION OF state_features UNTIL RUN TIME (AFTER Composition IS FULLY CONSTRUCTED) • composition.py: - run(): - set context.execution_phase to ContextFlags.PREPARING at start and reinstate entry context just before executing trials - _complete_init_of_partially_initialized_nodes(): - set needs_update_controller based on return value of ocm._update_state_input_ports_for_controller() • optmizationcontrolmechanism.py: _ _update_state_input_ports_for_controller(): - restrict call to _validate_state_features() to run time (ContextFlags.PROCESSING) - return True if passes, None otherwise - _validate_state_features(): - reinstate warning for number of state_features > agent_rep INPUT Nodes • test_control.py: -test_args_specific_to_ocm() - add run() to test to preciptitate error detected in _validate_state_feature_specs() - test_ocm_state_feature_specs_and_warnings_and_errors(): - reinstate 'too_many_inputs_warning' condition * - * - * • optimizationcontrolmechanism.py: deal with deferred INPUT nodes in state_features * - * - * • Passes all tests in test_ocm_state_feature_specs_and_warnings_and_errors() * - * - * • optimizationcontrolmechanism.py: - add _validate_input_nodes() - add _instantiate_pending_state_features() - STUB at present * - * - * • Passes all tests * • optimizationcontrolmechanism.py - state_features: updated after adding nodes to comp * - * • Passes all tests • optimizationcontrolmechanism.py - state_features: correct to properly include updated INPUT nodes * • GENUNINELY Passes all tests (some were commented out on the prior commit) • optimizationcontrolmechanism.py - _parse_state_feature_specs() & state_features: further fixes to properly include added INPUT nodes * - * - * - * • optimizationcontrolmechanism.py _parse_state_feature_specs() & _validate_state_features(): add mention of missing nodes to warning / error messages * • optimizationcontrolmechanism.py add _get_nodes_not_in_agent_rep() * - * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): enforce list spec for agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR * • composition.py: - add_node(): add call to _analyze_graph if not called from another Composition add method * - * • composition.py - add_node(): - comment out call to _analyze_graph: crashes in test_learning_output_shape() with ExecuteMode.LLVM • optimizationcontrolmechanism.py - state_features: clean up * • optimizationcontrolmechanism.py - _parse_state_feature_sepcs(): - removed argss: directly reference self.state_feature_specs and self.state_feature_functions * - * - * - * • optimizationcontrolmechanism.py: - _parse_state_feature_values_from_variable() - move to method on Class - populate with default variable for INPUT Nodes not specified as state_features * • optimizationcontrolmechanism.py: - remove _parse_state_feature_values_from_variable(): removed - add _state_feature_values_getter() * - * BEFORE REFACTORING TO INCLUDE ALL INPUT Nodes IN state_features (WITH VALUE OF NONE FOR ONES NOT SPECIFIED IN state_feature_specs) • optimizationcontrolmechanism.py - _state_feature_values_getter(): modify to handle calls in init and validation * PARTIAL REFACTORING TO INCLUDE ALL INPUT Nodes IN state_features (WITH VALUE OF NONE FOR ONES NOT SPECIFIED IN state_feature_specs) * REFACTORED TO INCLUDE ALL INPUT Nodes IN state_features (WITH VALUE OF NONE FOR ONES NOT SPECIFIED IN state_feature_specs) - PASSING test_ocm_state_feature_specs_and_warnings_and_errors() - WORKING on test_partial_deferred_init() * PASSES ALL test_control except lvoc * PASSES ALL TESTS • optimizationcontrolmechanism.py: - Mark potential mods for making state_feature_specs a Parameter (marked with 1/30/22) (with self.state_feature_specs that preserves user specifications) - implement self._state_feature_specs_parsed (in place of state_feature_specs Parameter for now) - restore self._specified_input_nodes_in_order (in place of state_feature_specs Parameter for now) - state.features property constructs dict from above attributes * PASSES ALL TESTS • optimizationcontrolmechanism.py: - Mark potential mods for making state_feature_specs a Parameter (marked with 1/30/22) (with self.state_feature_specs that preserves user specifications) - implement self._state_feature_specs_parsed (in place of state_feature_specs Parameter for now) - restore self._specified_input_nodes_in_order (in place of state_feature_specs Parameter for now) - state.features property constructs dict from above attributes * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): add tests for state_feature_values * • optimizationcontrolmechanism.py - docstring updates * • optimizationcontrolmechanism.py - docstring updates * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): support port specification dictionary as spec * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): fix bug in which state_input_ports were not getting instantiated if objective_mechanism or monitor_for_control were not specifiied * • optimizationcontrolmechanism.py - _parse_state_feature_specs(): fix bug in which state_input_ports were not getting instantiated if objective_mechanism or monitor_for_control were not specifiied * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): - add test conditions for monitor_for_control instead of objective_mechanism and neither * - * - * • optimizationcontrolmechanism.py: - add _assign_state_feature_functions: support tuple format for state_features * - * - * - * - * • test_control.py - add test_state_feature_function_specs() * - * - * • mechanism.py: - execute(): fix bug in which only primary input_port is tested for path_afferents to determine whether to update ports * - * • optimizationcontrolmechanism.py: - _assign_state_feature_function(): tuple overrides InputPort specification dict * - * - * - * - Co-authored-by: Katherine Mantel Co-authored-by: jdcpni --- ...on_Reward_rate_with_penalty_with_inputs.py | 2 +- .../Debug/Predator-Prey Sebastian REDUCED.py | 2 +- Scripts/Debug/Predator-Prey Sebastian.py | 2 +- Scripts/Debug/Umemoto_Feb.py | 2 +- Scripts/Debug/Umemoto_Feb2.py | 2 +- Scripts/Debug/new_umemoto.py | 2 +- .../predator_prey_opt/predator_prey_dmt.py | 2 +- Scripts/Debug/stability_flexibility_simple.py | 2 +- Scripts/Examples/EVC OCM.py | 2 +- Scripts/Examples/EVC-Gratton Composition.py | 2 +- .../Examples/EVC-Gratton-GaussianProcess.py | 2 +- Scripts/Examples/StabilityFlexibility.py | 2 +- .../Examples/Tutorial/Stroop Model - EVC.py | 4 +- .../Predator-Prey Model DQN LVOC.py | 2 +- .../core/components/mechanisms/mechanism.py | 2 +- .../modulatory/control/controlmechanism.py | 19 +- .../control/optimizationcontrolmechanism.py | 281 +++++++++++------- psyneulink/core/components/ports/port.py | 2 +- tests/composition/test_composition.py | 6 +- tests/composition/test_control.py | 125 ++++++-- tests/json/model_with_control.py | 2 +- tests/log/test_log.py | 2 +- 22 files changed, 316 insertions(+), 153 deletions(-) diff --git a/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py b/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py index ca0d4ebf933..4f7ac2be8d5 100644 --- a/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py +++ b/Scripts/Debug/Jason_Reward_rate_with_penalty_with_inputs.py @@ -204,7 +204,7 @@ def get_stroop_model(unit_noise_std=.01, dec_noise_std=.1): inp_task.input_port, reward.input_port, punish.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.1), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.1), objective_mechanism=objective_mech, function=pnl.GridSearch(), control_signals=[driftrate_control_signal, diff --git a/Scripts/Debug/Predator-Prey Sebastian REDUCED.py b/Scripts/Debug/Predator-Prey Sebastian REDUCED.py index c62a30a4078..15ec7e4928b 100644 --- a/Scripts/Debug/Predator-Prey Sebastian REDUCED.py +++ b/Scripts/Debug/Predator-Prey Sebastian REDUCED.py @@ -62,7 +62,7 @@ def get_new_episode_flag(): ocm = OptimizationControlMechanism(name='EVC', state_features=[trial_type_input_mech], - # state_feature_functions=FEATURE_FUNCTION, + # state_feature_function=FEATURE_FUNCTION, agent_rep=RegressionCFA( name='RegressionCFA', update_weights=BayesGLM(mu_0=0.5, sigma_0=0.1), diff --git a/Scripts/Debug/Predator-Prey Sebastian.py b/Scripts/Debug/Predator-Prey Sebastian.py index b332f104e11..491cb9f5a63 100644 --- a/Scripts/Debug/Predator-Prey Sebastian.py +++ b/Scripts/Debug/Predator-Prey Sebastian.py @@ -167,7 +167,7 @@ def get_action(variable=[[0,0],[0,0],[0,0]]): ocm = OptimizationControlMechanism(name='EVC', state_features=[trial_type_input_mech], - # state_feature_functions=FEATURE_FUNCTION, + # state_feature_function=FEATURE_FUNCTION, agent_rep=RegressionCFA( name='RegressionCFA', update_weights=BayesGLM(mu_0=0.5, sigma_0=0.1), diff --git a/Scripts/Debug/Umemoto_Feb.py b/Scripts/Debug/Umemoto_Feb.py index a8ba6f4b11b..e289c3225db 100644 --- a/Scripts/Debug/Umemoto_Feb.py +++ b/Scripts/Debug/Umemoto_Feb.py @@ -120,7 +120,7 @@ Umemoto_comp.add_model_based_optimizer(optimizer=pnl.OptimizationControlMechanism(agent_rep=Umemoto_comp, state_features=[Target_Stim.input_port, Distractor_Stim.input_port, Reward.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=1.0), + state_feature_function=pnl.AdaptiveIntegrator(rate=1.0), objective_mechanism=pnl.ObjectiveMechanism(monitor_for_control=[Reward, (Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], 1, -1)], ), diff --git a/Scripts/Debug/Umemoto_Feb2.py b/Scripts/Debug/Umemoto_Feb2.py index 1b75225342e..2815e4fbb2d 100644 --- a/Scripts/Debug/Umemoto_Feb2.py +++ b/Scripts/Debug/Umemoto_Feb2.py @@ -132,7 +132,7 @@ state_features=[Target_Stim.input_port, Distractor_Stim.input_port, Reward.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=1.0), + state_feature_function=pnl.AdaptiveIntegrator(rate=1.0), objective_mechanism=pnl.ObjectiveMechanism( monitor_for_control=[Reward, (Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], 1, -1)], diff --git a/Scripts/Debug/new_umemoto.py b/Scripts/Debug/new_umemoto.py index 621b0b3744a..e10028ee6ae 100644 --- a/Scripts/Debug/new_umemoto.py +++ b/Scripts/Debug/new_umemoto.py @@ -114,7 +114,7 @@ Umemoto_comp.add_model_based_optimizer(optimizer=pnl.OptimizationControlMechanism(agent_rep=Umemoto_comp, state_features={pnl.SHADOW_EXTERNAL_INPUTS: [Target_Stim, Distractor_Stim, Reward]}, - state_feature_functions=pnl.AdaptiveIntegrator(rate=1.0), + state_feature_function=pnl.AdaptiveIntegrator(rate=1.0), objective_mechanism=pnl.ObjectiveMechanism(monitor_for_control=[Reward, (Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], 1, -1)], ), diff --git a/Scripts/Debug/predator_prey_opt/predator_prey_dmt.py b/Scripts/Debug/predator_prey_opt/predator_prey_dmt.py index 0eb89fb2ed7..f1f4e80bcf1 100644 --- a/Scripts/Debug/predator_prey_opt/predator_prey_dmt.py +++ b/Scripts/Debug/predator_prey_opt/predator_prey_dmt.py @@ -195,7 +195,7 @@ def get_action(variable=[[0, 0], [0, 0], [0, 0]]): # ************************************** CONOTROL APPARATUS *********************************************************** self.ocm = OptimizationControlMechanism(name='EVC', state_features=[self.prey_pred_trial_input_mech, self.single_prey_trial_input_mech, self.double_prey_trial_input_mech], - # state_feature_functions=FEATURE_FUNCTION, + # state_feature_function=FEATURE_FUNCTION, agent_rep=RegressionCFA( update_weights=BayesGLM(mu_0=-0.0, sigma_0=0.0001), prediction_terms=[PV.F, PV.C, PV.COST] diff --git a/Scripts/Debug/stability_flexibility_simple.py b/Scripts/Debug/stability_flexibility_simple.py index b2bf6b133c1..f16443aafc0 100644 --- a/Scripts/Debug/stability_flexibility_simple.py +++ b/Scripts/Debug/stability_flexibility_simple.py @@ -164,7 +164,7 @@ def computeAccuracy(variable): meta_controller = pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, state_features=[inputLayer.input_port, stimulusInfo.input_port], - state_feature_functions=pnl.Buffer(history=100), + state_feature_function=pnl.Buffer(history=100), objective_mechanism=objective_mech, function=pnl.GridSearch(), control_signals=[signal]) diff --git a/Scripts/Examples/EVC OCM.py b/Scripts/Examples/EVC OCM.py index 362ac5e07db..6216f00dc2a 100644 --- a/Scripts/Examples/EVC OCM.py +++ b/Scripts/Examples/EVC OCM.py @@ -32,7 +32,7 @@ comp.add_model_based_optimizer(optimizer=OptimizationControlMechanism(name='OCM', agent_rep=comp, state_features=[Input.input_port, reward.input_port], - state_feature_functions=AdaptiveIntegrator(rate=0.5), + state_feature_function=AdaptiveIntegrator(rate=0.5), objective_mechanism=ObjectiveMechanism( name='OCM Objective Mechanism', function=LinearCombination(operation=PRODUCT), diff --git a/Scripts/Examples/EVC-Gratton Composition.py b/Scripts/Examples/EVC-Gratton Composition.py index 071ae433155..7d5a35f610e 100644 --- a/Scripts/Examples/EVC-Gratton Composition.py +++ b/Scripts/Examples/EVC-Gratton Composition.py @@ -65,7 +65,7 @@ state_features=[target_stim.input_port, flanker_stim.input_port, reward.input_port], - state_feature_functions=pnl.AdaptiveIntegrator( + state_feature_function=pnl.AdaptiveIntegrator( rate=1.0), objective_mechanism=objective_mech, function=pnl.GridSearch(), diff --git a/Scripts/Examples/EVC-Gratton-GaussianProcess.py b/Scripts/Examples/EVC-Gratton-GaussianProcess.py index 5148e22d8a9..32cd454e6ea 100644 --- a/Scripts/Examples/EVC-Gratton-GaussianProcess.py +++ b/Scripts/Examples/EVC-Gratton-GaussianProcess.py @@ -52,7 +52,7 @@ comp.add_linear_processing_pathway(task_execution_pathway) ocm = pnl.OptimizationControlMechanism(state_features=[Input, Reward], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), agent_rep=comp, # function=pnl.GaussianProcessOptimization, function=pnl.GridSearch, diff --git a/Scripts/Examples/StabilityFlexibility.py b/Scripts/Examples/StabilityFlexibility.py index 2a5f3b2c23f..1b5c5a1b254 100644 --- a/Scripts/Examples/StabilityFlexibility.py +++ b/Scripts/Examples/StabilityFlexibility.py @@ -177,7 +177,7 @@ def computeAccuracy(variable): meta_controller = pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, state_features=[inputLayer.input_port, stimulusInfo.input_port], - state_feature_functions=pnl.Buffer(history=3), + state_feature_function=pnl.Buffer(history=3), objective_mechanism=objective_mech, function=pnl.GridSearch(), control_signals=[signal]) diff --git a/Scripts/Examples/Tutorial/Stroop Model - EVC.py b/Scripts/Examples/Tutorial/Stroop Model - EVC.py index 1db6570ad1d..ef20720c04f 100644 --- a/Scripts/Examples/Tutorial/Stroop Model - EVC.py +++ b/Scripts/Examples/Tutorial/Stroop Model - EVC.py @@ -64,8 +64,8 @@ evc = OptimizationControlMechanism(name='EVC', agent_rep=Stroop_model, state_features=[color_input.input_port, word_input.input_port, reward.input_port], - state_feature_functions=AdaptiveIntegrator(rate=1.0), - # state_feature_functions=AdaptiveIntegrator, + state_feature_function=AdaptiveIntegrator(rate=1.0), + # state_feature_function=AdaptiveIntegrator, objective_mechanism= \ ObjectiveMechanism( name='EVC Objective Mechanism', diff --git a/Scripts/Models (Under Development)/Predator-Prey Model DQN LVOC.py b/Scripts/Models (Under Development)/Predator-Prey Model DQN LVOC.py index f6525841a6d..52db2a7a4fa 100644 --- a/Scripts/Models (Under Development)/Predator-Prey Model DQN LVOC.py +++ b/Scripts/Models (Under Development)/Predator-Prey Model DQN LVOC.py @@ -161,7 +161,7 @@ def get_action(variable=[[0,0],[0,0],[0,0]]): ocm = OptimizationControlMechanism(name='EVC', state_features=trial_type_input_mech, - state_feature_functions=FEATURE_FUNCTION, + state_feature_function=FEATURE_FUNCTION, agent_rep=RegressionCFA( update_weights=BayesGLM(mu_0=0.5, sigma_0=0.1), prediction_terms=[PV.F, PV.C, PV.COST] diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 616379ce75e..840c889f69a 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2480,7 +2480,7 @@ def execute(self, # Executing or simulating Composition, so get input by updating input_ports if (input is None and (context.execution_phase is not ContextFlags.IDLE) - and (self.input_port.path_afferents != [])): + and any(p.path_afferents for p in self.input_ports)): variable = self._update_input_ports(runtime_port_params[INPUT_PORT_PARAMS], context) # Direct call to execute Mechanism with specified input, so assign input to Mechanism's input_ports diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index 4ab4c7b82c8..117b7b49608 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1567,6 +1567,7 @@ def _instantiate_input_ports(self, input_ports=None, context=None): # of the objective_mechanism's constructor self._instantiate_objective_mechanism(input_ports, context=context) + # FIX: CONSOLIDATE THIS WITH SIMILAR HANDLING IN _instantiate_objective_mechanism AND ELSE BELOW # If no ObjectiveMechanism is specified, but items to monitor are specified, # assign an outcome_input_port for each item specified elif self.monitor_for_control: @@ -1595,7 +1596,6 @@ def _instantiate_input_ports(self, input_ports=None, context=None): from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection from psyneulink.core.components.mechanisms.processing.objectivemechanism import _parse_monitor_specs - self.aux_components = [] for i in range(len(projection_specs)): if option == SEPARATE: # Each outcome_input_port get its own Projection @@ -1606,10 +1606,21 @@ def _instantiate_input_ports(self, input_ports=None, context=None): self.aux_components.append(MappingProjection(sender=projection_specs[i], receiver=self.outcome_input_ports[outcome_port_index])) - # Nothing has been specified, so just instantiate the default OUTCOME InputPort + # Nothing has been specified, so just instantiate the default OUTCOME InputPort with any input_ports passed in else: - super()._instantiate_input_ports(context=context) + # # MODIFIED 1/30/21 OLD: + # super()._instantiate_input_ports(context=context) + # self.outcome_input_ports.append(self.input_ports[OUTCOME]) + # MODIFIED 1/30/21 NEW: + other_input_port_value_sizes = self._handle_arg_input_ports(other_input_ports)[0] + # Construct full list of InputPort specifications and sizes + input_ports = self.input_ports + other_input_ports + input_port_value_sizes = [[0]] + other_input_port_value_sizes + super()._instantiate_input_ports(context=context, + input_ports=input_ports, + reference_value=input_port_value_sizes) self.outcome_input_ports.append(self.input_ports[OUTCOME]) + # MODIFIED 1/30/21 END def _parse_monitor_for_control_input_ports(self, context): """Get outcome_input_port specification dictionaries for items specified in monitor_for_control. @@ -1714,7 +1725,7 @@ def _register_control_signal_type(self, context=None): ) def _instantiate_control_signals(self, context): - """Subclassess can override for class-specific implementation (see OptimizationControlMechanism for example)""" + """Subclasses can override for class-specific implementation (see OptimizationControlMechanism for example)""" output_port_specs = list(enumerate(self.output_ports)) for i, control_signal in output_port_specs: diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 45114fe0352..77816c34265 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -26,7 +26,7 @@ - `State Features ` - `agent_rep Composition ` - `agent_rep CompositionFunctionApproximator ` - - `State Feature Functions ` + - `State Feature Functions ` - `Outcome ` * `OptimizationControlMechanism_Structure` - `Agent Representation ` @@ -254,7 +254,7 @@ `state_input_ports ` that allow them to process inputs (e.g., modulate and/or intergrate them) before them as `state_feature_values state_feature_values ` (see `below - `). Note that assigning **state_features** explicilty + `). Note that assigning **state_features** explicitly overrides their automatic assignment, so that all required values must be specified, and this must be done accurate, as described below. @@ -276,8 +276,8 @@ For cases in which only a subset of the inputs to the Composition are relevant to its optimization (e.g., the others should be held constant), it is still the case that all must be specified as **state_features** (see note above). This can be handled several ways. One is by specifying (as required) **state_features** - for all of the inputs, and assigning *state_feature_functions** (see `below - `) such that those assigned to the desired + for all of the inputs, and assigning *state_feature_function** (see `below + `) such that those assigned to the desired inputs pass their values unmodified, while those for the inputs that are to be ignored return a constant value. Another approach, for cases in which the desired inputs pertain to a subset of Components in the Composition solely responsible for determining its `net_outcome `, is to assign those @@ -357,19 +357,19 @@ ` each time it is `evaluated `. .. _Optimization_Control_Mechanism_Tuple_State_Feature: - * *2-item tuple* -- the first item must be a `Port` or `Mechanism` specification, as described below; the - second item must be a `Function` that is assigned as the `function ` of the corresponding - `state_input_port ` (see `state feature functions - ` for additional details). + * *2-item tuple* -- the first item must be a `Port` or `Mechanism` specification, as described below; + the second item must be a `Function`, that is assigned as the `function ` of the + corresponding `state_input_port `; + this takes precedence over any other state_feature_function specifications (e.g., in an `InputPort + specification dictionary ` or the **state_feature_function** argument + of the OptimizationControlMechanism's constructor; see `state_feature_function + ` for additional details). - COMMENT: - FIX: CONFIRM THAT THIS WORKS .. _Optimization_Control_Mechanism_Input_Port_Dict_State_Feature: * *specification dictionary* -- an `InputPort specification dictionary ` can be used to configure the corresponding `state_input_port `, if `Parameters ` other than its `function ` need to be specified (e.g., its `name ` or more than a single `afferent Projection `). - COMMENT .. _Optimization_Control_Mechanism_Input_Port_State_Feature: * *InputPort specification* -- creates an `InputPort` that `shadows ` the input to @@ -437,31 +437,31 @@ order may produce errors that are unexpected or difficult to interpret. COMMENT: - FIX: CONFIRM THAT THE FOLLOWING ALL WORK + FIX: CONFIRM THAT THE FOLLOWING WORKS State features can also be added to an existing OptimizationControlMechanism using its `add_state_features` method. COMMENT -.. _OptimizationControlMechanism_State_Feature_Functions_Arg: - -* **state_feature_functions** -- specifies the `function(s) ` assigned to the `state_input_ports - ` created for each of the corresponding **state_features**. - If **state_feature_functions** is not specified, the identity function is assigned to all of the `state_input_ports - ` (whether those were created automatically or explicitly specified; - see `above `). However, other functions can be specified - individually for the `state_input_ports ` associated with each - state_feature. This can be useful, for example to provide an average or integrated value of prior inputs, to - select specific inputs for use (see `hint ` above), and/or use a - generative model of the environment to provide inputs to the `agent_rep ` - during the optimization process. This can be done by specifying the **state_feature_functions** argument with a - dict with keys that match each of the specifications in the **state_features** argument, and corresponding values - that specify the function to use for each. +.. _OptimizationControlMechanism_State_Feature_Function_Arg: + +* **state_feature_function** -- specifies a `function ` to be used as the default + function for `state_input_ports `. This is assigned as + the `function ` to any state_input_ports for which no other `Function` is specified; + i.e., in an InputPort specification dictionary ` or `2-item tuple + ` in the **state_features** argument (see `state_features + `). If either of the latter is specified, they override + the specification in **state_feature_function**. If it is *not* specified, then `LinearCombination` + (the standard default `Function` for an `InputPort`) is assigned to any `state_input_ports + ` that are not otherwise assigned a `Function`. + Specifying functions for `state_input_ports ` can be useful, + for example to provide an average or integrated value of prior inputs to the `agent_rep + `\\'s `evaluate ` method during the optimization + process, or to use a generative model of the environment to provide those inputs. .. note:: - A dict can be used to specify **state_feature_functions** only if **state_features** are specified explicitly - (see `above `). The dict must contain one entry for - each of the items specified in **state_features**, and the value returned by each function must preserve the - shape of its input, which must match that of the corresponding input to the Composition's `run - ` method (see `note ` above). + The value returned by a function assigned to the **state_feature_function** argument must preserve the + shape of its input, and must also accommodate the shape of the inputs to all of the `state_input_ports + ` to which it is assigned (see `note + ` above). .. _OptimizationControlMechanism_Outcome_Args: @@ -1009,12 +1009,12 @@ __all__ = [ 'OptimizationControlMechanism', 'OptimizationControlMechanismError', - 'AGENT_REP', 'STATE_FEATURES', 'STATE_FEATURE_FUNCTIONS', 'RANDOMIZATION_CONTROL_SIGNAL', 'NUM_ESTIMATES' + 'AGENT_REP', 'STATE_FEATURES', 'STATE_FEATURE_FUNCTION', 'RANDOMIZATION_CONTROL_SIGNAL', 'NUM_ESTIMATES' ] AGENT_REP = 'agent_rep' STATE_FEATURES = 'state_features' -STATE_FEATURE_FUNCTIONS = 'state_feature_functions' +STATE_FEATURE_FUNCTION = 'state_feature_function' RANDOMIZATION_CONTROL_SIGNAL = 'RANDOMIZATION_CONTROL_SIGNAL' RANDOM_VARIABLES = 'random_variables' NUM_ESTIMATES = 'num_estimates' @@ -1072,7 +1072,7 @@ class OptimizationControlMechanism(ControlMechanism): """OptimizationControlMechanism( \ agent_rep=None, \ state_features=None, \ - state_feature_functions=None, \ + state_feature_function=None, \ monitor_for_control=None, \ objective_mechanism=None, \ function=GridSearch, \ @@ -1104,11 +1104,10 @@ class OptimizationControlMechanism(ControlMechanism): 's `evaluate ` method whent it is executed. See `state_features ` for details of specification. - state_feature_functions : Function or function : default None - specifies the `function ` assigned the `InputPort` in `state_input_ports - ` assigned to each **state_feature** - (see `state_feature_functions ` - for additional details). + state_feature_function : Function or function : default None + specifies the `function ` to use as the default function for the `state_input_ports + ` created for the corresponding **state_features** (see + `state_feature_function ` for additional details). agent_rep : None or Composition : default None or Composition to which OptimizationControlMechanism is assigned specifies the `Composition` used by `evaluate_agent_rep ` @@ -1212,8 +1211,13 @@ class OptimizationControlMechanism(ControlMechanism): state_feature_values : 2d array the current value of each item of the OptimizationControlMechanism's `state_input_ports - (see `OptimizationControlMechanism_State_Features` for - additional details). + (see `OptimizationControlMechanism_State_Features` + for additional details). + + state_feature_function : Function of function + determines the `function ` used as the default function for + `state_input_ports ` (see `state_feature_function + ` for additional details). state_input_ports : ContentAddressableList lists the OptimizationControlMechanism's `InputPorts ` that receive `Projections ` @@ -1224,7 +1228,7 @@ class OptimizationControlMechanism(ControlMechanism): ` method (see `OptimizationControlMechanism_State_Features` for additional details). num_state_input_ports : int - cantains the number of `state_input_ports `. + contains the number of `state_input_ports `. outcome_input_ports : ContentAddressableList lists the OptimizationControlMechanism's `OutputPorts ` that receive `Projections ` @@ -1490,8 +1494,8 @@ class Parameters(ControlMechanism.Parameters): :default value: None :type: ``dict`` - state_feature_functions - see `state_feature_functions ` + state_feature_function + see `state_feature_function ` :default value: None :type: @@ -1505,7 +1509,7 @@ class Parameters(ControlMechanism.Parameters): outcome_input_ports_option = Parameter(CONCATENATE, stateful=False, loggable=False, structural=True) state_input_ports = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) # state_feature_specs = Parameter(None, stateful=False, loggable=False, read_only=True, structural=True) - state_feature_functions = Parameter(None, reference=True, stateful=False, loggable=False) + state_feature_function = Parameter(None, reference=True, stateful=False, loggable=False) function = Parameter(GridSearch, stateful=False, loggable=False) search_function = Parameter(None, stateful=False, loggable=False) search_space = Parameter(None, read_only=True) @@ -1535,7 +1539,8 @@ class Parameters(ControlMechanism.Parameters): saved_samples = None saved_values = None - # # MODIFIED 1/30/22 NEW: MAY BE NEEDED IF state_feature_specs IS MADE A PARAMETER; SHOULD SET spec ATTRIBUTE + # # MODIFIED 1/30/22 NEW: FIX - MAY BE NEEDED IF state_feature_specs -> Parameter, + # WHICH SHOULD SET spec ATTRIBUTE # def _parse_state_feature_specs(self, state_features): # return (state_features if isinstance(state_features, (dict, set)) else convert_to_list(state_features)) # MODIFIED 1/30/22 END @@ -1545,7 +1550,7 @@ class Parameters(ControlMechanism.Parameters): def __init__(self, agent_rep=None, state_features: tc.optional(tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort))) = None, - state_feature_functions: tc.optional(tc.optional(tc.any(dict, is_function_type))) = None, + state_feature_function: tc.optional(tc.optional(tc.any(dict, is_function_type))) = None, function=None, num_estimates = None, random_variables = None, @@ -1559,6 +1564,8 @@ def __init__(self, **kwargs): """Implement OptimizationControlMechanism""" + from psyneulink.core.compositions.composition import Composition + # Legacy warnings and conversions for k in kwargs.copy(): if k == 'features': @@ -1573,20 +1580,29 @@ def __init__(self, kwargs.pop('features') continue if k == 'feature_function': - if state_feature_functions: - warnings.warn(f"Both 'feature_function' and 'state_feature_functions' were specified in the " + if state_feature_function: + warnings.warn(f"Both 'feature_function' and 'state_feature_function' were specified in the " f"constructor for an {self.__class__.__name__}. Note: 'feature_function' has been " - f"deprecated; 'state_feature_functions' ({state_feature_functions}) will be used.") + f"deprecated; 'state_feature_function' ({state_feature_function}) will be used.") else: warnings.warn(f"'feature_function' was specified in the constructor for an" f"{self.__class__.__name__}; Note: 'feature_function' has been deprecated; " - f"please use 'state_feature_functions' in the future.") - state_feature_functions = kwargs['feature_function'] + f"please use 'state_feature_function' in the future.") + state_feature_function = kwargs['feature_function'] kwargs.pop('feature_function') continue - # MODIFIED 1/30/22 OLD: REMOVE IF state_feature_specs -> Parameter AND SET IN Parameter._parse_state_feature_specs - self.state_feature_specs = (state_features if isinstance(state_features, (dict, set)) + # FIX: 1/30/22 - REMOVE IF state_feature_specs -> Parameter AND SET IN Parameter._parse_state_feature_specs + # # MODIFIED 1/30/22 OLD: + # self.state_feature_specs = (state_features if isinstance(state_features, (dict, set)) + # else convert_to_list(state_features)) + # MODIFIED 1/30/22 NEW: + # Enclose state_features in a list unless it is already a list, set, or state_feature specification dict + self.state_feature_specs = (state_features if (isinstance(state_features, set) + or (isinstance(state_features, dict) + and (any(isinstance(key, (Port, Mechanism, Composition)) + for key in state_features) + or SHADOW_INPUTS in state_features))) else convert_to_list(state_features)) # MODIFIED 1/30/22 END @@ -1624,10 +1640,10 @@ def __init__(self, super().__init__( agent_rep=agent_rep, - # # MODIFIED 1/30/22 NEW: MAY NEED IF state_feature_specs -> Parameter + # # MODIFIED 1/30/22 NEW: FIX - MAY NEED IF state_feature_specs -> Parameter # state_feature_specs=state_features, # MODIFIED 1/30/22 END - state_feature_functions=state_feature_functions, + state_feature_function=state_feature_function, function=function, num_estimates=num_estimates, num_trials_per_estimate = num_trials_per_estimate, @@ -1655,19 +1671,19 @@ def _validate_params(self, request_set, target_set=None, context=None): raise OptimizationControlMechanismError(f"The '{AGENT_REP}' arg of an {self.__class__.__name__} " f"must be either a {Composition.__name__} or a sublcass of one") - elif request_set[STATE_FEATURE_FUNCTIONS]: + elif request_set[STATE_FEATURE_FUNCTION]: state_feats = request_set.pop(STATE_FEATURES, None) - state_feat_fcts = request_set.pop(STATE_FEATURE_FUNCTIONS, None) + state_feat_fcts = request_set.pop(STATE_FEATURE_FUNCTION, None) # If no or only one item is specified in state_features, only one state_function is allowed if ((not state_feats or len(convert_to_list(state_feats))==1) and len(convert_to_list(state_feat_fcts))!=1): raise OptimizationControlMechanismError(f"Only one function is allowed to be specified for " - f"the '{STATE_FEATURE_FUNCTIONS}' arg of {self.name} " + f"the '{STATE_FEATURE_FUNCTION}' arg of {self.name} " f"if either no only one items is specified for its " f"'{STATE_FEATURES}' arg.") if len(convert_to_list(state_feat_fcts))>1 and not isinstance(state_feat_fcts, dict): raise OptimizationControlMechanismError(f"The '{STATE_FEATURES}' arg of {self.name} contains more " - f"than one item, so its '{STATE_FEATURE_FUNCTIONS}' arg " + f"than one item, so its '{STATE_FEATURE_FUNCTION}' arg " f"must be either only a single function (applied to all " f"{STATE_FEATURES}) or a dict with entries of the form " f":.") @@ -1675,7 +1691,7 @@ def _validate_params(self, request_set, target_set=None, context=None): invalid_fct_specs = [fct_spec for fct_spec in state_feat_fcts if fct_spec not in state_feats] if invalid_fct_specs: raise OptimizationControlMechanismError(f"The following entries of the dict specified for " - f"'{STATE_FEATURE_FUNCTIONS} of {self.name} have keys that " + f"'{STATE_FEATURE_FUNCTION} of {self.name} have keys that " f"do not match any InputPorts specified in its " f"{STATE_FEATURES} arg: {invalid_fct_specs}.") @@ -1692,7 +1708,7 @@ def _validate_params(self, request_set, target_set=None, context=None): # FIX: CONSIDER GETTING RID OF THIS METHOD ENTIRELY, AND LETTING state_input_ports # BE HANDLED ENTIRELY BY _update_state_input_ports_for_controller def _instantiate_input_ports(self, context=None): - """Instantiate InputPorts for state_features (with state_feature_functions if specified). + """Instantiate InputPorts for state_features (with state_feature_function if specified). This instantiates the OptimizationControlMechanism's `state_input_ports; these are used to provide input to the agent_rep when its evaluate method is called @@ -1701,7 +1717,7 @@ def _instantiate_input_ports(self, context=None): ControlMechanism._instantiate_input_ports in the call to super(). InputPorts are constructed for **state_features** by calling _parse_state_feature_specs - with them and **state_feature_functions** arguments of the OptimizationControlMechanism constructor. + with them and **state_feature_function** arguments of the OptimizationControlMechanism constructor. The constructed state_input_ports are passed to ControlMechanism_instantiate_input_ports(), which appends them to the InputPort(s) that receive input from the **objective_mechanism* (if specified) or **monitor_for_control** ports (if **objective_mechanism** is not specified). @@ -1726,7 +1742,7 @@ def _instantiate_input_ports(self, context=None): # ITS PRIMARY IS USED (SEE SCRATCH PAD FOR EXAMPLES) if not self.state_feature_specs: # If agent_rep is CompositionFunctionApproximator, warn if no state_features specified. - # Note: if agent rep is Composition, state_input_ports and any state_feature_functions specified + # Note: if agent rep is Composition, state_input_ports and any state_feature_function specified # are assigned in _update_state_input_ports_for_controller. if self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR: warnings.warn(f"No '{STATE_FEATURES}' specified for use with `agent_rep' of {self.name}") @@ -1830,7 +1846,7 @@ def _validate_input_nodes(self, nodes, enforce=None): else: warnings.warn(message) - # FIX: 1/29/22 - REFACTOR TO SUPPORT TUPLE AND InportPort SPECIFICATIONI DICT FOR MULT. PROJS. TO STATE_INPUT_PORT + # FIX: 1/29/22 - REFACTOR TO SUPPORT TUPLE AND InportPort SPECIFICATION DICT FOR MULT. PROJS. TO STATE_INPUT_PORT def _parse_state_feature_specs(self, context=None): """Parse entries of state_features specifications into InputPort spec dictionaries. @@ -1858,7 +1874,7 @@ def _parse_state_feature_specs(self, context=None): - set: INPUT Nodes for which shadowing is implemented - SHADOW_INPUTS dict: single entry with key='SHADOW_INPUTS" and values=list of sources (see above). - Assign functions specified in **state_feature_functions** to InputPorts for all state_features + Assign functions specified in **state_feature_function** to InputPorts for all state_features Return list of InputPort specification dictionaries for state_input_ports """ @@ -1866,10 +1882,13 @@ def _parse_state_feature_specs(self, context=None): from psyneulink.core.compositions.composition import Composition, NodeRole # Agent rep's input Nodes and their names agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) + # The following are all "full" lists; that is, there is an entry corresponding to every INPUT node of agent_rep # List of INPUT Nodes for which state_features are specified, ordered according to agent_rep.nodes self._specified_input_nodes_in_order = [] - # List of parsed state_feature_specs + # List of parsed state_feature_specs (vs. user provided specs) self._state_feature_specs_parsed = [] + # List of assigned state_feature_function (vs. user provided specs) + self._state_feature_functions = [] # VALIDATION AND WARNINGS ----------------------------------------------------------------------------------- @@ -1971,22 +1990,18 @@ def _parse_specs(state_feature_specs, spec_str="list"): self._num_state_feature_specs = max(num_specs, num_nodes) for i in range(self._num_state_feature_specs): # NODE & NODE_NAME + # (and specs for CFA and any nodes not yet in agent_rep) spec_name = None + state_feature_fct = None if i < num_nodes: - # # MODIFIED 1/29/22 OLD: - # # Node should be in agent_rep, so use that to be sure - # node = agent_rep_input_nodes[i] - # node_name = node.name - # MODIFIED 1/29/22 NEW: - # FIX: 1/29/22 - NEED TO HANDLE agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR HERE # Node should be in agent_rep, so use that to be sure if self.agent_rep_type == COMPOSITION: node = agent_rep_input_nodes[i] + # Assign spec as node for CompositionFunctionApproximator else: spec = state_feature_specs[i] node = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner node_name = node.name - # MODIFIED 1/29/22 END else: # Node not (yet) in agent_rep, so uses its node name spec = state_feature_specs[i] @@ -2006,13 +2021,23 @@ def _parse_specs(state_feature_specs, spec_str="list"): spec_name = spec.full_name else: spec_name = spec.name + elif isinstance(spec, dict): + spec_name = spec[NAME] if NAME in spec else f"STATE FEATURE INPUT for {node_name}" + elif isinstance(spec, tuple): + state_feature_fct = spec[1] + spec = spec[0] + elif spec is not None: + assert False, f"PROGRAM ERROR: unrecognized form of state_feature specification for {self.name}" else: # Fewer specifications than number of INPUT Nodes, # the remaining ones may be specified later, but for now assume they are meant to be ignored spec = None - self._specified_input_nodes_in_order.append(node) + self._state_feature_specs_parsed.append(spec) + self._state_feature_functions.append(state_feature_fct) + self._specified_input_nodes_in_order.append(node) spec_names.append(spec_name) + return spec_names or [] # LIST spec @@ -2048,14 +2073,14 @@ def _parse_specs(state_feature_specs, spec_str="list"): input_port_names = _parse_specs(specs) # SET spec - # Treat as specification of INPUT Nodes to be shadowed: - # - construct an InputPort dict with SHADOW_INPUTS as its key, and specs in a list as its value + # Treat as specification of INPUT Nodes to be shadowed: + # - construct an InputPort dict with SHADOW_INPUTS as its key, and specs in a list as its value elif isinstance(state_feature_specs, set): # All nodes must be INPUT nodes of agent_rep, that are to be shadowed, self._validate_input_nodes(state_feature_specs) # specs = [node if node in state_feature_specs else None for node in agent_rep_input_nodes] # FIX: 1/29/22 - - # THIS IS DANGEROUS -- SHOULD REPLACE ONCE TUPLE FORMAT IS IMPLEMENTED OR USE INPUTPORT SPECIF DICT + # THIS IS DANGEROUS -- SHOULD REPLACE ONCE TUPLE FORMAT IS IMPLEMENTED OR USE InputPort SPECIF DICT # IT WORKS FOR NESTED COMPS WITH A SIGNLE INPUT NODE OF THEIR OWN # BUT IF A NESTED COMP HAS MORE THAN ONE INPUT NODE, THIS WILL RETURN MORE THAN ONE NODE IN PLACE OF # THE NESTED COMP AND THUS GET OUT OF ALIGNMENT WITH NUMBER OF INPUT NODES FOR AGENT_REP @@ -2067,8 +2092,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): all_nested_input_nodes.extend(get_inputs_for_nested_comp(node)) else: all_nested_input_nodes.append(node) - # Get specs in order of agent_rep INPUT Nodes, with any not in agent_rep at end - # NEED TO ADD Nones TO LIST IF NOT SPECIFIED + # Get specs ordered by agent_rep INPUT Nodes, with any not in agent_rep at end and None for any not included agent_rep_nodes = self._get_agent_rep_input_nodes() nodes = [node if node in all_nested_input_nodes else None for node in agent_rep_nodes] nodes.extend([node for node in all_nested_input_nodes if node not in agent_rep_nodes]) @@ -2090,7 +2114,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): PARAMS: {DEFAULT_INPUT: DEFAULT_VARIABLE} } else: - spec = spec[0] + spec = spec[0] # _parse_shadow_inputs(self, spec) returns a list, even when passed a single item # If optimization uses Composition, assume that shadowing a Mechanism means shadowing its primary InputPort if isinstance(spec, Mechanism): if self.agent_rep_type == COMPOSITION: @@ -2106,6 +2130,15 @@ def _parse_specs(state_feature_specs, spec_str="list"): spec = spec.output_port # Update Mechanism spec with Port self._state_feature_specs_parsed[i] = spec + if isinstance(spec, dict): + # Note : need to handle this here so that FUNCTION is taken into account when VALUE is assigned + # in call to _parse_port_spec() below + if self._state_feature_functions[i]: + # Assign function to dict + spec[FUNCTION] = self._state_feature_functions[i] + # Clear function from PARAMS subdict if specified + if PARAMS in spec: + spec[PARAMS].pop(FUNCTION, None) parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) if not parsed_spec[NAME]: @@ -2113,24 +2146,48 @@ def _parse_specs(state_feature_specs, spec_str="list"): if parsed_spec[PARAMS] and SHADOW_INPUTS in parsed_spec[PARAMS]: # Composition._update_shadow_projections will take care of PROJECTIONS specification - parsed_spec[PARAMS].update({INTERNAL_ONLY:True, - PROJECTIONS:None}) + parsed_spec[PARAMS][INTERNAL_ONLY]=True, + parsed_spec[PARAMS][PROJECTIONS]=None - # GET FEATURE FUNCTIONS ----------------------------------------------------------------------------- + # Assign function for state_input_port if specified--------------------------------------------------- + parsed_spec = self._assign_state_feature_function(parsed_spec, i) - if self.state_feature_functions: - if isinstance(self.state_feature_functions, dict) and spec in self.state_feature_functions: - feature_functions = self.feature_functions.copy() - feat_fct = feature_functions.pop(spec) - else: - feat_fct = self.state_feature_functions - parsed_spec.update({FUNCTION: self._parse_state_feature_function(feat_fct)}) parsed_spec = [parsed_spec] # so that extend works below - state_input_port_specs.extend(parsed_spec) return state_input_port_specs + def _assign_state_feature_function(self, specification_dict, idx=None): + """Assign any specified state_feature_function to corresponding state_input_ports + idx is index into self._state_feature_functions; if None, use self.state_feature_function specified by user + Specification in InputPort specification dictionary or **state_features** tuple + takes precedence over **state_feature_function** specification. + Return state_input_port_dicts with FUNCTION entries added as appropriate. + """ + + # Note: state_feature_function has been validated in _validate_params + default_function = self.state_feature_function # User specified in constructor + try: + if self._state_feature_functions: + assert len(self._state_feature_functions) == self._num_state_feature_specs, \ + f"PROGRAM ERROR: Length of _state_feature_functions for {self.name} should be same " \ + f"as number of state_input_port_dicts passed to _assign_state_feature_function" + state_feature_functions = self._state_feature_functions + except AttributeError: + # state_features assigned automatically in _update_state_input_ports_for_controller, + # so _state_feature_functions (for individual state_features) not created + state_feature_functions = None + + fct = state_feature_functions[idx] if state_feature_functions else None + if fct: + # Don't worry about original FUNCTION spec in PARAMS entry of InputPort specification dict -- handled above + specification_dict[FUNCTION] = self._parse_state_feature_function(fct) + elif default_function and FUNCTION not in specification_dict[PARAMS]: + # Assign **state_feature_function** (aka default_function) if specified and no other has been specified + specification_dict[FUNCTION] = self._parse_state_feature_function(default_function) + + return specification_dict + def _parse_state_feature_function(self, feature_function): if isinstance(feature_function, Function): return copy.deepcopy(feature_function) @@ -2172,7 +2229,7 @@ def _update_state_input_ports_for_controller(self, context=None): - if no state_features specified, assign a state_input_port for every InputPort of every INPUT Node of agent_rep (note: shadow Projections for all state_input_ports are created in Composition._update_shadow_projections()). - - assign state_feature_functions to relevant state_input_ports (same function for all if no state_features + - assign state_feature_function to relevant state_input_ports (same function for all if no state_features are specified or only one state_function is specified; otherwise, use dict for specifications). Return True if successful, None if not performed. @@ -2209,39 +2266,45 @@ def _update_state_input_ports_for_controller(self, context=None): elif not self.state_input_ports: # agent_rep is Composition, but no state_features have been specified, # so assign a state_input_port to shadow every InputPort of every INPUT node of agent_rep - shadow_input_ports = [] # Get list of nodes with any nested Comps that are INPUT Nodes replaced with their respective INPUT Nodes # (as those are what need to be shadowed) + shadowed_input_ports = [] for node in self._get_agent_rep_input_nodes(comp_as_node=False): for input_port in node.input_ports: if input_port.internal_only: continue # if isinstance(input_port.owner, CompositionInterfaceMechanism): # input_port = input_port. - shadow_input_ports.append(input_port) + shadowed_input_ports.append(input_port) + # Instantiate state_input_ports local_context = Context(source=ContextFlags.METHOD) - state_input_ports_to_add = [] + state_input_ports = [] # for input_port in input_ports_not_specified: - for input_port in shadow_input_ports: + for input_port in shadowed_input_ports: input_port_name = f"{SHADOW_INPUT_NAME}{input_port.owner.name}[{input_port.name}]" params = {SHADOW_INPUTS: input_port, INTERNAL_ONLY:True} - # Note: state_feature_functions has been validated _validate_params - # to have only a single function in for model-based agent_rep - if self.state_feature_functions: - params.update({FUNCTION: self._parse_state_feature_function(self.state_feature_functions)}) - state_input_ports_to_add.append(_instantiate_port(name=input_port_name, - port_type=InputPort, - owner=self, - reference_value=input_port.value, - params=params, - context=local_context)) - self.add_ports(state_input_ports_to_add, + if self.state_feature_function: + # Use **state_feature_function** if specified by user in constructor + params = self._assign_state_feature_function(params) + state_input_port = _instantiate_port(name=input_port_name, + port_type=InputPort, + owner=self, + reference_value=input_port.value, + params=params, + context=local_context) + state_input_ports.append(state_input_port) + + self.add_ports(state_input_ports, update_variable=False, context=local_context) - self.state_input_ports.extend(state_input_ports_to_add) + + # Assign OptimizationControlMechanism attributes + self.state_input_ports.data = state_input_ports + self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) + self._state_feature_specs_parsed = [input_port.shadow_inputs for input_port in self.state_input_ports] self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) self._state_feature_specs_parsed = [input_port.shadow_inputs for input_port in self.state_input_ports] diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index e8e6d057a41..9784d0db5fc 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -166,7 +166,7 @@ `: * *PROJECTIONS*:List[<`projection specification `>,...] - the list must contain a one or more `Projection specifications ` to or from + the list must contain one or more `Projection specifications ` to or from the Port, and/or `ModulatorySignals ` from which it should receive projections (see `Port_Projections` below). diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 8161ab97cac..4d73cfdb42e 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -1282,7 +1282,7 @@ def test_properties(self, control_spec): agent_rep=comp, num_estimates=2, state_features=[Input.input_port, Reward.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.1), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.1), monitor_for_control=[Reward, Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], Decision.output_ports[pnl.RESPONSE_TIME]], @@ -4255,7 +4255,7 @@ def test_invalid_projection_deletion_when_nesting_comps(self): pnl.OptimizationControlMechanism( agent_rep=ocomp, state_features=[oa.input_port], - # state_feature_functions=pnl.Buffer(history=2), + # state_feature_function=pnl.Buffer(history=2), name="Controller", objective_mechanism=ocomp_objective_mechanism, function=pnl.GridSearch(direction=pnl.MINIMIZE), @@ -4275,7 +4275,7 @@ def test_invalid_projection_deletion_when_nesting_comps(self): pnl.OptimizationControlMechanism( agent_rep=icomp, state_features=[ia.input_port], - # state_feature_functions=pnl.Buffer(history=2), + # state_feature_function=pnl.Buffer(history=2), name="Controller", objective_mechanism=icomp_objective_mechanism, function=pnl.GridSearch(direction=pnl.MAXIMIZE), diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index c3c50fc4bad..5dbab50ea69 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -177,7 +177,7 @@ def test_deferred_init(self, control_spec): comp.add_controller(controller=pnl.OptimizationControlMechanism( agent_rep=comp, state_features=[reward.input_port, Input.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), monitor=[reward, @@ -872,7 +872,12 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c @pytest.mark.control @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x[0] for x in state_feature_args]) - def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_args): + @pytest.mark.parametrize('obj_mech', [ + 'obj_mech', + 'mtr_for_ctl', + None + ]) + def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_args, obj_mech): test_condition = state_feature_args[0] message_1 = state_feature_args[1] @@ -922,9 +927,12 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'comp_in_list_spec':[icomp, oa.output_port, [3,1,2]], # FIX: REMOVE ONCE TUPLE FORMAT SUPPORTED 'comp_in_shadow_inupts_spec':{pnl.SHADOW_INPUTS:[icomp, oa, ob]} } + objective_mechanism = [ic,ib] if obj_mech == 'obj_mech' else None + monitor_for_control = [ic] if obj_mech == 'mtr_for_ctl' else None # Needs to be a single item for GridSearch state_features = state_features_dict[test_condition] ocm = pnl.OptimizationControlMechanism(state_features=state_features, - objective_mechanism=[ic,ib], + objective_mechanism=objective_mechanism, + monitor_for_control=monitor_for_control, function=pnl.GridSearch(), control_signals=[pnl.ControlSignal(modulates=(pnl.SLOPE,ia), allocation_samples=[10, 20, 30]), @@ -1044,6 +1052,86 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ocomp.run() assert message_1 in str(error.value) + @pytest.mark.control + @pytest.mark.parametrize('state_fct_assignments', [ + 'partial_w_dict', + 'partial_w_params_dict', + 'tuple_override_dict', + 'tuple_override_params_dict', + 'all', + None + ]) + def test_state_feature_function_specs(self, state_fct_assignments): + + fct_a = pnl.AdaptiveIntegrator + fct_b = pnl.Buffer(history=2) + fct_c = pnl.SimpleIntegrator + A = pnl.ProcessingMechanism(name='A') + B = pnl.ProcessingMechanism(name='B') + C = pnl.ProcessingMechanism(name='C') + R = pnl.ProcessingMechanism(name='D') + + # FIX: ALSO TEST TUPLE OVERRIDE OF SPECIFICATION DICT + if state_fct_assignments == 'partial_w_dict': + state_features = [{pnl.PROJECTIONS: A, + pnl.FUNCTION: fct_a}, + (B, fct_b), + C] + state_feature_function = fct_c + elif state_fct_assignments == 'partial_w_params_dict': + state_features = [{pnl.PARAMS: {pnl.PROJECTIONS: A, + pnl.FUNCTION: fct_a}}, + (B, fct_b), + C] + state_feature_function = fct_c + elif state_fct_assignments == 'tuple_override_dict': + state_features = [({pnl.PROJECTIONS: A, + pnl.FUNCTION: pnl.Buffer}, fct_a), + (B, fct_b), + C] + state_feature_function = fct_c + elif state_fct_assignments == 'tuple_override_params_dict': + state_features = [({pnl.PARAMS: {pnl.PROJECTIONS: A, + pnl.FUNCTION: pnl.Buffer}}, fct_a), + (B, fct_b), + C] + state_feature_function = fct_c + elif state_fct_assignments == 'all': + state_features = [(A.output_port, fct_a), (B, fct_b), (C, fct_c)] + state_feature_function = None + else: + state_features = [A, B, C] + state_feature_function = None + + comp = pnl.Composition(name='comp', pathways=[[A,R],[B,R],[C,R]]) + ocm = pnl.OptimizationControlMechanism(state_features=state_features, + state_feature_function=state_feature_function, + function=pnl.GridSearch(), + # monitor_for_control=A, + control_signals=[pnl.ControlSignal(modulates=(pnl.SLOPE, A), + allocation_samples=[10, 20, 30])]) + comp.add_controller(ocm) + if state_fct_assignments: + assert isinstance(ocm.state_input_ports[0].function, fct_a) + assert isinstance(ocm.state_input_ports[1].function, fct_b.__class__) + assert isinstance(ocm.state_input_ports[2].function, fct_c) + inputs = {A:[1,2], B:[1,2], C:[1,2]} + result = comp.run(inputs=inputs, context='test') + assert result == [[24.]] + assert all(np.allclose(expected, actual) + for expected, actual in zip(ocm.parameters.state_feature_values.get('test'), + [[20],[[1],[2]],[3]])) + else: + assert isinstance(ocm.state_input_ports[0].function, pnl.LinearCombination) + assert isinstance(ocm.state_input_ports[1].function, pnl.LinearCombination) + assert isinstance(ocm.state_input_ports[2].function, pnl.LinearCombination) + inputs = {A:[1,2], B:[1,2], C:[1,2]} + result = comp.run(inputs=inputs, context='test') + assert result == [[24.]] + assert all(np.allclose(expected, actual) + for expected, actual in zip(ocm.parameters.state_feature_values.get('test'), + [[2],[2],[2]])) + @pytest.mark.control def test_ocm_state_and_state_dict(self): ia = pnl.ProcessingMechanism(name='IA') @@ -1211,7 +1299,7 @@ def test_lvoc_features_function(self): c._analyze_graph() lvoc = pnl.OptimizationControlMechanism(agent_rep=pnl.RegressionCFA, state_features=[m1.input_ports[0], m1.input_ports[1], m2.input_port, m2], - state_feature_functions=pnl.LinearCombination(offset=10.0), + state_feature_function=pnl.LinearCombination(offset=10.0), objective_mechanism=pnl.ObjectiveMechanism( monitor=[m1, m2]), function=pnl.GradientOptimization(max_iterations=1), @@ -1253,7 +1341,7 @@ def test_multilevel_ocm_gridsearch_conflicting_directions(self, mode, benchmark) pnl.OptimizationControlMechanism( agent_rep=ocomp, state_features=[oa.input_port], - # state_feature_functions=pnl.Buffer(history=2), + # state_feature_function=pnl.Buffer(history=2), name="Controller", objective_mechanism=pnl.ObjectiveMechanism( monitor=ib.output_port, @@ -1270,7 +1358,7 @@ def test_multilevel_ocm_gridsearch_conflicting_directions(self, mode, benchmark) pnl.OptimizationControlMechanism( agent_rep=icomp, state_features=[ia.input_port], - # state_feature_functions=pnl.Buffer(history=2), + # state_feature_function=pnl.Buffer(history=2), name="Controller", objective_mechanism=pnl.ObjectiveMechanism( monitor=ib.output_port, @@ -1316,7 +1404,7 @@ def test_multilevel_ocm_gridsearch_maximize(self, mode, benchmark): pnl.OptimizationControlMechanism( agent_rep=ocomp, state_features=[oa.input_port], - # state_feature_functions=pnl.Buffer(history=2), + # state_feature_function=pnl.Buffer(history=2), name="Controller", objective_mechanism=pnl.ObjectiveMechanism( monitor=ib.output_port, @@ -1335,7 +1423,7 @@ def test_multilevel_ocm_gridsearch_maximize(self, mode, benchmark): pnl.OptimizationControlMechanism( agent_rep=icomp, state_features=[ia.input_port], - # state_feature_functions=pnl.Buffer(history=2), + # state_feature_function=pnl.Buffer(history=2), name="Controller", objective_mechanism=pnl.ObjectiveMechanism( monitor=ib.output_port, @@ -1383,7 +1471,7 @@ def test_multilevel_ocm_gridsearch_minimize(self, mode, benchmark): pnl.OptimizationControlMechanism( agent_rep=ocomp, state_features=[oa.input_port], - # state_feature_functions=pnl.Buffer(history=2), + # state_feature_function=pnl.Buffer(history=2), name="oController", objective_mechanism=pnl.ObjectiveMechanism( monitor=ib.output_port, @@ -1402,7 +1490,7 @@ def test_multilevel_ocm_gridsearch_minimize(self, mode, benchmark): pnl.OptimizationControlMechanism( agent_rep=icomp, state_features=[ia.input_port], - # state_feature_functions=pnl.Buffer(history=2), + # state_feature_function=pnl.Buffer(history=2), name="iController", objective_mechanism=pnl.ObjectiveMechanism( monitor=ib.output_port, @@ -1541,7 +1629,7 @@ def test_two_tier_ocm(self): pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, state_features=[taskLayer.input_port, stimulusInfo.input_port], - state_feature_functions=pnl.Buffer(history=2), + state_feature_function=pnl.Buffer(history=2), name="Controller", objective_mechanism=pnl.ObjectiveMechanism( monitor=[(pnl.PROBABILITY_UPPER_THRESHOLD, @@ -1567,7 +1655,7 @@ def test_two_tier_ocm(self): outerComposition.add_controller( pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, state_features=[taskLayer.input_port, stimulusInfo.input_port], - state_feature_functions=pnl.Buffer(history=2), + state_feature_function=pnl.Buffer(history=2), name="OuterController", objective_mechanism=pnl.ObjectiveMechanism( monitor=[(pnl.PROBABILITY_UPPER_THRESHOLD, decisionMaker)], @@ -1990,7 +2078,7 @@ def test_evc(self): comp.add_controller(controller=pnl.OptimizationControlMechanism( agent_rep=comp, state_features=[reward.input_port, Input.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), monitor=[reward, @@ -2129,7 +2217,7 @@ def test_evc_gratton(self): state_features=[target_stim.input_port, flanker_stim.input_port, reward.input_port], - state_feature_functions=pnl.AdaptiveIntegrator( + state_feature_function=pnl.AdaptiveIntegrator( rate=1.0), objective_mechanism=objective_mech, function=pnl.GridSearch(), @@ -2274,7 +2362,7 @@ def test_laming_validation_specify_control_signals(self): controller=pnl.OptimizationControlMechanism( agent_rep=comp, state_features=[reward.input_port, Input.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), monitor=[ @@ -2412,7 +2500,7 @@ def test_stateful_mechanism_in_simulation(self): controller=pnl.OptimizationControlMechanism( agent_rep=comp, state_features=[reward.input_port, Input.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), monitor=[ @@ -2622,7 +2710,7 @@ def test_model_based_ocm_with_buffer(self): objective_mech = pnl.ObjectiveMechanism(monitor=[B]) ocm = pnl.OptimizationControlMechanism(agent_rep=comp, state_features=[A.input_port], - state_feature_functions=pnl.Buffer(history=2), + state_feature_function=pnl.Buffer(history=2), objective_mechanism=objective_mech, function=pnl.GridSearch(), control_signals=[control_signal]) @@ -2822,7 +2910,7 @@ def computeAccuracy(trialInformation): # Sets trial history for simulations over specified signal search parameters metaController = pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, state_features=[taskLayer.input_port, stimulusInfo.input_port], - state_feature_functions=pnl.Buffer(history=10), + state_feature_function=pnl.Buffer(history=10), name="Controller", objective_mechanism=objectiveMechanism, function=pnl.GridSearch(), @@ -3080,6 +3168,7 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ ocomp.add_controller(ocm) ocomp.run() + class TestSampleIterator: def test_int_step(self): diff --git a/tests/json/model_with_control.py b/tests/json/model_with_control.py index 10b401d1f62..ecb4b8a1ac1 100644 --- a/tests/json/model_with_control.py +++ b/tests/json/model_with_control.py @@ -48,7 +48,7 @@ controller=pnl.OptimizationControlMechanism( agent_rep=comp, state_features=[Input.input_port, reward.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), monitor=[ diff --git a/tests/log/test_log.py b/tests/log/test_log.py index 37e6a50bf06..9cb42a1c37f 100644 --- a/tests/log/test_log.py +++ b/tests/log/test_log.py @@ -1165,7 +1165,7 @@ def node_logged_in_simulation(self): controller=pnl.OptimizationControlMechanism( agent_rep=comp, state_features=[Input.input_port, reward.input_port], - state_feature_functions=pnl.AdaptiveIntegrator(rate=0.5), + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), objective_mechanism=pnl.ObjectiveMechanism( function=pnl.LinearCombination(operation=pnl.PRODUCT), monitor=[ From a8927fa22b774af7c96906caf8edf7405e13b065 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Thu, 3 Feb 2022 22:42:00 -0500 Subject: [PATCH 119/285] Test/ocm/state feat fct dict (#2303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • test_control.py: - test_state_feature_function_specs(): add test InputPort specification dict in state_feature dict * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): support InputPort specification dictionary in state_features specification • test_control.py: - test_state_feature_function_specs(): add port_spec_dict_in_feat_dict condition * - Co-authored-by: jdcpni --- .../control/optimizationcontrolmechanism.py | 17 ++++++++--------- tests/composition/test_control.py | 8 +++++++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 77816c34265..4eee233942b 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -2023,6 +2023,10 @@ def _parse_specs(state_feature_specs, spec_str="list"): spec_name = spec.name elif isinstance(spec, dict): spec_name = spec[NAME] if NAME in spec else f"STATE FEATURE INPUT for {node_name}" + if FUNCTION in spec: + state_feature_fct = spec[FUNCTION] + elif PARAMS in spec and FUNCTION in spec[PARAMS]: + state_feature_fct = spec[PARAMS][FUNCTION] elif isinstance(spec, tuple): state_feature_fct = spec[1] spec = spec[0] @@ -2119,7 +2123,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): if isinstance(spec, Mechanism): if self.agent_rep_type == COMPOSITION: # FIX: 11/29/21: MOVE THIS TO _parse_shadow_inputs - # (ADD ARG TO THAT FOR DOING SO, OR RESTRICTING TO INPUTPORTS IN GENERAL) + # (ADD ARG TO THAT FOR DOING SO, OR RESTRICT TO InputPorts IN GENERAL) if len(spec.input_ports)!=1: raise OptimizationControlMechanismError(f"A Mechanism ({spec.name}) is specified in the " f"'{STATE_FEATURES}' arg for {self.name} that has " @@ -2131,12 +2135,9 @@ def _parse_specs(state_feature_specs, spec_str="list"): # Update Mechanism spec with Port self._state_feature_specs_parsed[i] = spec if isinstance(spec, dict): - # Note : need to handle this here so that FUNCTION is taken into account when VALUE is assigned - # in call to _parse_port_spec() below + # Note: clear any functions specified; will be assigned in _assign_state_feature_function if self._state_feature_functions[i]: - # Assign function to dict - spec[FUNCTION] = self._state_feature_functions[i] - # Clear function from PARAMS subdict if specified + spec.pop(FUNCTION, None) if PARAMS in spec: spec[PARAMS].pop(FUNCTION, None) parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) @@ -2162,6 +2163,7 @@ def _assign_state_feature_function(self, specification_dict, idx=None): idx is index into self._state_feature_functions; if None, use self.state_feature_function specified by user Specification in InputPort specification dictionary or **state_features** tuple takes precedence over **state_feature_function** specification. + Assignment of function to dict specs handled above, so skip here Return state_input_port_dicts with FUNCTION entries added as appropriate. """ @@ -2177,15 +2179,12 @@ def _assign_state_feature_function(self, specification_dict, idx=None): # state_features assigned automatically in _update_state_input_ports_for_controller, # so _state_feature_functions (for individual state_features) not created state_feature_functions = None - fct = state_feature_functions[idx] if state_feature_functions else None if fct: - # Don't worry about original FUNCTION spec in PARAMS entry of InputPort specification dict -- handled above specification_dict[FUNCTION] = self._parse_state_feature_function(fct) elif default_function and FUNCTION not in specification_dict[PARAMS]: # Assign **state_feature_function** (aka default_function) if specified and no other has been specified specification_dict[FUNCTION] = self._parse_state_feature_function(default_function) - return specification_dict def _parse_state_feature_function(self, feature_function): diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 5dbab50ea69..70ef2009e0c 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1058,6 +1058,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'partial_w_params_dict', 'tuple_override_dict', 'tuple_override_params_dict', + 'port_spec_dict_in_feat_dict', 'all', None ]) @@ -1071,7 +1072,6 @@ def test_state_feature_function_specs(self, state_fct_assignments): C = pnl.ProcessingMechanism(name='C') R = pnl.ProcessingMechanism(name='D') - # FIX: ALSO TEST TUPLE OVERRIDE OF SPECIFICATION DICT if state_fct_assignments == 'partial_w_dict': state_features = [{pnl.PROJECTIONS: A, pnl.FUNCTION: fct_a}, @@ -1096,6 +1096,12 @@ def test_state_feature_function_specs(self, state_fct_assignments): (B, fct_b), C] state_feature_function = fct_c + elif state_fct_assignments == 'port_spec_dict_in_feat_dict': + state_features = {A:{pnl.PROJECTIONS: A, + pnl.FUNCTION: fct_a}, + B: ({pnl.PROJECTIONS: B}, fct_b), + C: C} + state_feature_function = fct_c elif state_fct_assignments == 'all': state_features = [(A.output_port, fct_a), (B, fct_b), (C, fct_c)] state_feature_function = None From 075a7189771fb8db6d16dbdc3dc0f48424d485ac Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 4 Feb 2022 01:03:38 -0500 Subject: [PATCH 120/285] TransferMechanism: fix integration_rate validation (#2304) shape of integration_rate should at least be checked against exact shape of default variable otherwise some valid configs fail (ex. rate and variable = [[0], [0]]) --- .../components/mechanisms/processing/transfermechanism.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index b484af0c3fa..7cb05c78adc 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -1410,8 +1410,11 @@ def _validate_params(self, request_set, target_set=None, context=None): # Validate INTEGRATION_RATE: if INTEGRATION_RATE in target_set and target_set[INTEGRATION_RATE] is not None: integration_rate = np.array(target_set[INTEGRATION_RATE]) - if (not np.isscalar(integration_rate.tolist()) - and integration_rate.shape != self.defaults.variable.squeeze().shape): + if ( + not np.isscalar(integration_rate.tolist()) + and integration_rate.shape != self.defaults.variable.shape + and integration_rate.shape != self.defaults.variable.squeeze().shape + ): raise TransferError(f"{repr(INTEGRATION_RATE)} arg for {self.name} ({integration_rate}) " f"must be either an int or float, or have the same shape " f"as its {VARIABLE} ({self.defaults.variable}).") From 642216557554be2b0c21986524c76c15c7214c16 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 5 Feb 2022 21:59:52 -0500 Subject: [PATCH 121/285] tests/llvm: Add more explicit test cases to isclose test Signed-off-by: Jan Vesely --- tests/llvm/test_helpers.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/llvm/test_helpers.py b/tests/llvm/test_helpers.py index 7d41f9930fc..7235d46a617 100644 --- a/tests/llvm/test_helpers.py +++ b/tests/llvm/test_helpers.py @@ -102,15 +102,27 @@ def test_helper_fclamp_const(mode): @pytest.mark.llvm @pytest.mark.parametrize('mode', ['CPU', pytest.param('PTX', marks=pytest.mark.cuda)]) -def test_helper_is_close(mode): +@pytest.mark.parametrize('rtol,atol', + [[0, 0], [None, None], [None, 100], [2, None]]) +@pytest.mark.parametrize('var1,var2', + [[1, 1], [1, 100], [1,2], [-4,5], [0, -100], [-1,-2], + [[1,1,1,-4,0,-1], [1,100,2,5,-100,-2]] + ]) +def test_helper_is_close(mode, var1, var2, rtol, atol): + + tolerance = {} + if rtol is not None: + tolerance['rtol'] = rtol + if atol is not None: + tolerance['atol'] = atol + with pnlvm.LLVMBuilderContext.get_current() as ctx: double_ptr_ty = ir.DoubleType().as_pointer() func_ty = ir.FunctionType(ir.VoidType(), [double_ptr_ty, double_ptr_ty, double_ptr_ty, ctx.int32_ty]) - # Create clamp function - custom_name = ctx.get_unique_name("all_close") + custom_name = ctx.get_unique_name("is_close") function = ir.Function(ctx.module, func_ty, name=custom_name) in1, in2, out, count = function.args block = function.append_basic_block(name="entry") @@ -122,7 +134,7 @@ def test_helper_is_close(mode): val2_ptr = b1.gep(in2, [index]) val1 = b1.load(val1_ptr) val2 = b1.load(val2_ptr) - close = pnlvm.helpers.is_close(ctx, b1, val1, val2) + close = pnlvm.helpers.is_close(ctx, b1, val1, val2, **tolerance) out_ptr = b1.gep(out, [index]) out_val = b1.select(close, val1.type(1), val1.type(0)) res = b1.select(close, out_ptr.type.pointee(1), @@ -131,14 +143,12 @@ def test_helper_is_close(mode): builder.ret_void() - vec1 = copy.deepcopy(VECTOR) - tmp = np.random.rand(DIM_X) - tmp[0::2] = vec1[0::2] - vec2 = np.asfarray(tmp) + vec1 = np.atleast_1d(np.asfarray(var1)) + vec2 = np.atleast_1d(np.asfarray(var2)) assert len(vec1) == len(vec2) res = np.empty_like(vec2) - ref = np.isclose(vec1, vec2) + ref = np.isclose(vec1, vec2, **tolerance) bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) if mode == 'CPU': ct_ty = ctypes.POINTER(bin_f.byref_arg_types[0]) @@ -146,7 +156,7 @@ def test_helper_is_close(mode): ct_vec2 = vec2.ctypes.data_as(ct_ty) ct_res = res.ctypes.data_as(ct_ty) - bin_f(ct_vec1, ct_vec2, ct_res, DIM_X) + bin_f(ct_vec1, ct_vec2, ct_res, len(res)) else: bin_f.cuda_wrap_call(vec1, vec2, res, np.int32(DIM_X)) From d9254b30fc419c672c6aa8f944ef9536234a7443 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 5 Feb 2022 22:11:42 -0500 Subject: [PATCH 122/285] llvm/helpers: all_close should compare elements across both input arrays Add more comprehensive testing, including different tolerances. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/helpers.py | 2 +- tests/llvm/test_helpers.py | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/psyneulink/core/llvm/helpers.py b/psyneulink/core/llvm/helpers.py index dce10f5ba15..22e45c1e236 100644 --- a/psyneulink/core/llvm/helpers.py +++ b/psyneulink/core/llvm/helpers.py @@ -196,7 +196,7 @@ def all_close(ctx, builder, arr1, arr2, rtol=1e-05, atol=1e-08): builder.store(all_ptr.type.pointee(1), all_ptr) with array_ptr_loop(builder, arr1, "all_close") as (b1, idx): val1_ptr = b1.gep(arr1, [idx.type(0), idx]) - val2_ptr = b1.gep(arr1, [idx.type(0), idx]) + val2_ptr = b1.gep(arr2, [idx.type(0), idx]) val1 = b1.load(val1_ptr) val2 = b1.load(val2_ptr) res_close = is_close(ctx, b1, val1, val2, rtol, atol) diff --git a/tests/llvm/test_helpers.py b/tests/llvm/test_helpers.py index 7235d46a617..d5a9e9112fa 100644 --- a/tests/llvm/test_helpers.py +++ b/tests/llvm/test_helpers.py @@ -166,10 +166,26 @@ def test_helper_is_close(mode, var1, var2, rtol, atol): @pytest.mark.llvm @pytest.mark.parametrize('mode', ['CPU', pytest.param('PTX', marks=pytest.mark.cuda)]) -def test_helper_all_close(mode): +@pytest.mark.parametrize('rtol,atol', + [[0, 0], [None, None], [None, 100], [2, None]]) +@pytest.mark.parametrize('var1,var2', + [[1, 1], [1, 100], [1,2], [-4,5], [0, -100], [-1,-2], + [[1,1,1,-4,0,-1], [1,100,2,5,-100,-2]] + ]) +def test_helper_all_close(mode, var1, var2, atol, rtol): + + tolerance = {} + if rtol is not None: + tolerance['rtol'] = rtol + if atol is not None: + tolerance['atol'] = atol + + vec1 = np.atleast_1d(np.asfarray(var1)) + vec2 = np.atleast_1d(np.asfarray(var2)) + assert len(vec1) == len(vec2) with pnlvm.LLVMBuilderContext.get_current() as ctx: - arr_ptr_ty = ir.ArrayType(ir.DoubleType(), DIM_X).as_pointer() + arr_ptr_ty = ir.ArrayType(ir.DoubleType(), len(vec1)).as_pointer() func_ty = ir.FunctionType(ir.VoidType(), [arr_ptr_ty, arr_ptr_ty, ir.IntType(32).as_pointer()]) @@ -179,15 +195,13 @@ def test_helper_all_close(mode): block = function.append_basic_block(name="entry") builder = ir.IRBuilder(block) - all_close = pnlvm.helpers.all_close(ctx, builder, in1, in2) + all_close = pnlvm.helpers.all_close(ctx, builder, in1, in2, **tolerance) res = builder.select(all_close, out.type.pointee(1), out.type.pointee(0)) builder.store(res, out) builder.ret_void() - vec1 = copy.deepcopy(VECTOR) - vec2 = copy.deepcopy(VECTOR) - ref = np.allclose(vec1, vec2) + ref = np.allclose(vec1, vec2, **tolerance) bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) if mode == 'CPU': ct_ty = ctypes.POINTER(bin_f.byref_arg_types[0]) From e7905809d5217bf6ca6cc1db5bd6d700d3831d79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Feb 2022 06:43:49 +0000 Subject: [PATCH 123/285] github-actions(deps): bump actions/setup-python from 2.3.1 to 2.3.2 (#2306) --- .github/workflows/pnl-ci-docs.yml | 2 +- .github/workflows/pnl-ci.yml | 2 +- .github/workflows/test-release.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pnl-ci-docs.yml b/.github/workflows/pnl-ci-docs.yml index 869c9dd94f5..97da06f2ef1 100644 --- a/.github/workflows/pnl-ci-docs.yml +++ b/.github/workflows/pnl-ci-docs.yml @@ -61,7 +61,7 @@ jobs: branch: master - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 + uses: actions/setup-python@v2.3.2 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.python-architecture }} diff --git a/.github/workflows/pnl-ci.yml b/.github/workflows/pnl-ci.yml index 3f26414982b..904c1df10e0 100644 --- a/.github/workflows/pnl-ci.yml +++ b/.github/workflows/pnl-ci.yml @@ -46,7 +46,7 @@ jobs: fetch-depth: 10 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 + uses: actions/setup-python@v2.3.2 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.python-architecture }} diff --git a/.github/workflows/test-release.yml b/.github/workflows/test-release.yml index 5be1d382304..653b0b99bb0 100644 --- a/.github/workflows/test-release.yml +++ b/.github/workflows/test-release.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 + uses: actions/setup-python@v2.3.2 with: python-version: ${{ matrix.python-version }} @@ -84,7 +84,7 @@ jobs: path: dist/ - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 + uses: actions/setup-python@v2.3.2 with: python-version: ${{ matrix.python-version }} From 8578bd1eedc52b66e0827b7ce5f9a069e478ca96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Feb 2022 16:22:27 +0000 Subject: [PATCH 124/285] requirements: update pytest requirement from <6.2.6 to <7.0.1 (#2307) --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 48bea50167c..ffb5ee405e0 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,5 +1,5 @@ jupyter<=1.0.0 -pytest<6.2.6 +pytest<7.0.1 pytest-benchmark<3.4.2 pytest-cov<3.0.1 pytest-helpers-namespace<2021.12.30 From 05ab651d2d4f9062b554c8f6ab6e0aec525c79b9 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 6 Feb 2022 19:36:26 -0500 Subject: [PATCH 125/285] Fix/comp and mech/input variable (#2310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • composition.py: get_input_format(): add template option to return formatted dict for use as input to run() * - * - * • composition.py, mechanism.py: - add external_input_variables properties - add get_input_variables() methods replace use of input.value with input.varible for validating/sizing inputs * - * - * • function.py: Function_Base: - add input_shape_template attribute (as experiment for now) * • mechanism.py: - add: _input_shape_template, default_input_shape, input_shape, get_input_shape, default_external_input_shape, external_input_shape - _get_variable_from_input(): refactor to use above instead of variable or value * • mechanism.py, inputport.py: move from mechanism to inputport: _input_shape_template, default_input_shape, input_shape, get_input_shape * - * - * - * - * - * - * • mechanism.py: - get_variable_from_input(): - refactor to assign each item of input to corresponding input_port.variable and each input_port.value to result of executing its function with the variable * - * - * - * - * • Passes all tests * • Passes all tests • function.py: input_shape_template -> changes_shape * - * • test_composition.py: add: test_get_input_format() Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- .../core/components/functions/function.py | 19 ++- .../nonstateful/combinationfunctions.py | 18 ++- .../functions/stateful/memoryfunctions.py | 9 +- .../core/components/mechanisms/mechanism.py | 91 +++++++++++- .../control/optimizationcontrolmechanism.py | 2 +- psyneulink/core/components/ports/inputport.py | 26 ++++ psyneulink/core/compositions/composition.py | 129 ++++++++++++++---- .../mechanisms/processing/integrator/ddm.py | 4 +- .../processing/transfer/kwtamechanism.py | 7 - tests/components/test_component.py | 9 +- tests/composition/test_composition.py | 35 ++++- tests/composition/test_control.py | 12 +- tests/composition/test_learning.py | 4 +- tests/composition/test_report.py | 31 +++-- tests/functions/test_memory.py | 8 +- tests/mechanisms/test_ddm_mechanism.py | 16 ++- tests/mechanisms/test_kwta.py | 5 +- .../test_recurrent_transfer_mechanism.py | 6 +- tests/mechanisms/test_transfer_mechanism.py | 6 +- 19 files changed, 344 insertions(+), 93 deletions(-) diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index 98e5a63003a..43e3fc3dc0b 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -155,7 +155,7 @@ from psyneulink.core.globals.keywords import ( ARGUMENT_THERAPY_FUNCTION, AUTO_ASSIGN_MATRIX, EXAMPLE_FUNCTION_TYPE, FULL_CONNECTIVITY_MATRIX, FUNCTION_COMPONENT_CATEGORY, FUNCTION_OUTPUT_TYPE, FUNCTION_OUTPUT_TYPE_CONVERSION, HOLLOW_MATRIX, - IDENTITY_MATRIX, INVERSE_HOLLOW_MATRIX, NAME, PREFERENCE_SET_NAME, RANDOM_CONNECTIVITY_MATRIX + IDENTITY_MATRIX, INVERSE_HOLLOW_MATRIX, NAME, PREFERENCE_SET_NAME, RANDOM_CONNECTIVITY_MATRIX, VALUE, VARIABLE ) from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import REPORT_OUTPUT_PREF, is_pref_set @@ -519,9 +519,12 @@ class Function_Base(Function): specifies whether `function output type conversion ` is enabled. output_type : FunctionOutputType : None - used to specify the return type for the `function `; `functionOuputTypeConversion` + used to determine the return type for the `function `; `functionOuputTypeConversion` must be enabled and implemented for the class (see `FunctionOutputType ` for details). + + changes_shape : bool : False + specifies whether the return value of the function is different than the shape of its `variable . Used to determine whether the shape of the inputs to the `Component` to which the function is assigned should be based on the `variable ` of the function or its `value `. COMMENT owner : Component @@ -568,11 +571,18 @@ class Parameters(Function.Parameters): :default value: False :type: ``bool`` + changes_shape + see `changes_shape ` + + :default value: False + :type: bool + output_type see `output_type ` :default value: FunctionOutputType.DEFAULT :type: `FunctionOutputType` + """ variable = Parameter(np.array([0]), read_only=True, pnl_internal=True, constructor_argument='default_variable') @@ -585,6 +595,11 @@ class Parameters(Function.Parameters): ) enable_output_type_conversion = Parameter(False, stateful=False, loggable=False, pnl_internal=True) + changes_shape = Parameter(False, stateful=False, loggable=False, pnl_internal=True) + def _validate_changes_shape(self, param): + if not isinstance(param, bool): + return f'must be a bool.' + # Note: the following enforce encoding as 1D np.ndarrays (one array per variable) variableEncodingDim = 1 diff --git a/psyneulink/core/components/functions/nonstateful/combinationfunctions.py b/psyneulink/core/components/functions/nonstateful/combinationfunctions.py index 3556e2b054c..e6d087c908e 100644 --- a/psyneulink/core/components/functions/nonstateful/combinationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/combinationfunctions.py @@ -42,7 +42,7 @@ ADDITIVE_PARAM, ARRANGEMENT, COMBINATION_FUNCTION_TYPE, COMBINE_MEANS_FUNCTION, CONCATENATE_FUNCTION, \ DEFAULT_VARIABLE, EXPONENTS, LINEAR_COMBINATION_FUNCTION, MULTIPLICATIVE_PARAM, OFFSET, OPERATION, \ PREDICTION_ERROR_DELTA_FUNCTION, PRODUCT, REARRANGE_FUNCTION, REDUCE_FUNCTION, SCALE, SUM, WEIGHTS, \ - PREFERENCE_SET_NAME + PREFERENCE_SET_NAME, VARIABLE from psyneulink.core.globals.utilities import convert_to_np_array, is_numeric, np_array_less_than_2d, parameter_spec from psyneulink.core.globals.context import ContextFlags from psyneulink.core.globals.parameters import Parameter @@ -179,6 +179,12 @@ class Parameters(CombinationFunction.Parameters): Attributes ---------- + changes_shape + see `changes_shape ` + + :default value: True + :type: bool + offset see `offset ` @@ -193,6 +199,7 @@ class Parameters(CombinationFunction.Parameters): """ scale = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) + changes_shape = Parameter(True, stateful=False, loggable=False, pnl_internal=True) @tc.typecheck def __init__(self, @@ -679,6 +686,12 @@ class Parameters(CombinationFunction.Parameters): :default value: None :type: + changes_shape + see `changes_shape ` + + :default value: True + :type: bool + offset see `offset ` @@ -708,6 +721,7 @@ class Parameters(CombinationFunction.Parameters): operation = SUM scale = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) offset = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) + changes_shape = Parameter(True, stateful=False, loggable=False, pnl_internal=True) @tc.typecheck def __init__(self, @@ -1178,7 +1192,7 @@ def __init__(self, ) def _validate_variable(self, variable, context=None): - """Insure that all items of list or np.ndarray in variable are of the same length + """Insure that all items of list or np.array in variable are of the same length Args: variable: diff --git a/psyneulink/core/components/functions/stateful/memoryfunctions.py b/psyneulink/core/components/functions/stateful/memoryfunctions.py index dc77e18cee6..d64a1acd1c3 100644 --- a/psyneulink/core/components/functions/stateful/memoryfunctions.py +++ b/psyneulink/core/components/functions/stateful/memoryfunctions.py @@ -44,7 +44,7 @@ from psyneulink.core.globals.keywords import \ ADDITIVE_PARAM, BUFFER_FUNCTION, MEMORY_FUNCTION, COSINE, \ ContentAddressableMemory_FUNCTION, DictionaryMemory_FUNCTION, \ - MIN_INDICATOR, MULTIPLICATIVE_PARAM, NEWEST, NOISE, OLDEST, OVERWRITE, RATE, RANDOM + MIN_INDICATOR, MULTIPLICATIVE_PARAM, NEWEST, NOISE, OLDEST, OVERWRITE, RATE, RANDOM, VARIABLE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.utilities import \ @@ -187,6 +187,12 @@ class Parameters(StatefulFunction.Parameters): :default value: numpy.array([], dtype=float64) :type: ``numpy.ndarray`` + changes_shape + see `changes_shape ` + + :default value: True + :type: bool + noise see `noise ` @@ -206,6 +212,7 @@ class Parameters(StatefulFunction.Parameters): ) history = None initializer = Parameter(np.array([]), pnl_internal=True) + changes_shape = Parameter(True, stateful=False, loggable=False, pnl_internal=True) @tc.typecheck diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 840c889f69a..2309bdab87d 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1086,6 +1086,7 @@ import numpy as np import typecheck as tc +import psyneulink from psyneulink.core import llvm as pnlvm from psyneulink.core.components.component import Component from psyneulink.core.components.functions.function import FunctionOutputType @@ -1301,6 +1302,15 @@ class Mechanism_Base(Mechanism): `internal_only `; these receive `inputs from a Composition ` if the Mechanism is one of its `INPUT` `Nodes `. + external_input_shape : List[List or 1d np.array] + list showing shapes of inputs expected for the Mechanism's `input_ports `. + Each item corresponds to an expected `path_afferent Projection ` and its shape the + expected `value ` of that `Projection`. + + external_input_variables : List[List or 1d np.array] + list of the `variable `\\s of the Mechanism's `external_input_ports + `. + external_input_values : List[List or 1d np.array] list of the `value `\\s of the Mechanism's `external_input_ports `. @@ -2586,6 +2596,15 @@ def execute(self, return value def _get_variable_from_input(self, input, context=None): + """Return array of results from each InputPort function executed with corresponding input item as its variable + This is called when Mechanism is executed on its own (e.g., during init or from the command line). + It: + - bypasses call to Port._update(), thus ignoring any afferent Projections assigned to the Mechanism; + - assigns each item of **input** to variable of corresponding InputPort; + - executes function of each InputPort using corresponding item of input as its variable; + - returns array of values generated by execution of each InputPort function. + """ + input = convert_to_np_array(input, dimension=2) num_inputs = np.size(input, 0) num_input_ports = len(self.input_ports) @@ -2601,13 +2620,29 @@ def _get_variable_from_input(self, input, context=None): "its number of input_ports ({2})". format(num_inputs, self.name, num_input_ports )) for input_item, input_port in zip(input, self.input_ports): - if len(input_port.defaults.value) == len(input_item): - input_port.parameters.value._set(input_item, context) + if input_port.default_input_shape.size == np.array(input_item).size: + from psyneulink.core.compositions.composition import RunError + + # Assign input_item as input_port.variable + input_port.parameters.variable._set(np.atleast_2d(input_item), context) + + # Call input_port._execute with newly assigned variable and assign result to input_port.value + base_error_msg = f"Input to '{self.name}' ({input_item}) is incompatible " \ + f"with its corresponding {InputPort.__name__} ({input_port.full_name})" + try: + input_port.parameters.value._set( + input_port._execute(input_port.parameters.variable.get(context), context), + context) + except (RunError,TypeError) as error: + raise MechanismError(f"{base_error_msg}: '{error.args[0]}.'") + except: + raise MechanismError(f"{base_error_msg}.") else: raise MechanismError(f"Length ({len(input_item)}) of input ({input_item}) does not match " - f"required length ({len(input_port.defaults.variable)}) for input " + f"required length ({input_port.default_input_shape.size}) for input " f"to {InputPort.__name__} {repr(input_port.name)} of {self.name}.") + # Return values of input_ports for use as variable of Mechanism return convert_to_np_array(self.get_input_values(context)) def _update_input_ports(self, runtime_input_port_params=None, context=None): @@ -3822,6 +3857,17 @@ def _get_port_value_labels(self, port_type, context=None): def input_port(self): return self.input_ports[0] + def get_input_variables(self, context=None): + # FIX: 2/4/22 THIS WOULD PARALLEL get_input_values BUT MAY NOT BE NEEDED: + # input_variables = [] + # for input_port in self.input_ports: + # if "LearningSignal" in input_port.name: + # input_variables.append(input_port.parameters.variable.get(context).flatten()) + # else: + # input_variables.append(input_port.parameters.variable.get(context)) + # return input_variables + return [input_port.parameters.variable.get(context) for input_port in self.input_ports] + @property def input_values(self): try: @@ -3845,6 +3891,45 @@ def external_input_ports(self): except (TypeError, AttributeError): return None + @property + def external_input_shape(self): + """Alias for _default_external_input_shape""" + return self._default_external_input_shape + + @property + def _default_external_input_shape(self): + try: + shape = [] + for input_port in self.input_ports: + if input_port.internal_only or input_port.default_input: + continue + if input_port._input_shape_template == VARIABLE: + shape.append(input_port.defaults.variable) + elif input_port._input_shape_template == VALUE: + shape.append(input_port.defaults.value) + else: + assert False, f"PROGRAM ERROR: bad changes_shape in attempt to assign " \ + f"default_external_input_shape for '{input_port.name}' of '{self.name}." + return shape + except (TypeError, AttributeError): + return None + + @property + def external_input_variables(self): + """Returns variables of all external InputPorts that belong to the Mechanism""" + try: + return [input_port.variable for input_port in self.input_ports if not input_port.internal_only] + except (TypeError, AttributeError): + return None + + @property + def default_external_input_variables(self): + try: + return [input_port.defaults.variable for input_port in self.input_ports if not input_port.internal_only] + except (TypeError, AttributeError): + return None + # MODIFIED 2/3/22 END + @property def external_input_values(self): try: diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 4eee233942b..73f52169b3f 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -2419,7 +2419,7 @@ def _validate_state_features(self): # Ensure state_features are compatible with input format for agent_rep Composition try: # FIX: 1/10/22 - ?USE self.agent_rep.external_input_values FOR CHECK? - # Call these to check for errors in construcing inputs dict + # Call these to check for errors in constructing inputs dict inputs = self.agent_rep._build_predicted_inputs_dict(None, self) self.agent_rep._parse_input_dict(inputs) except RunError as error: diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 6abf114a679..3655423c0a2 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -1347,6 +1347,32 @@ def get_label(self, context=None): label_dictionary = {} return self._get_value_label(label_dictionary, self.owner.input_ports, context=context) + @property + def _input_shape_template(self): + try: + if self.function.changes_shape: + return VARIABLE + else: + return VALUE + except: + assert False, f"PROGRAM ERROR: Missing or unrecognized 'changes_shape' attribute for " \ + f"('{self.function.name}') of '{self.name}'." + + @property + def default_input_shape(self): + if self._input_shape_template == VARIABLE: + return self.defaults.variable + elif self._input_shape_template == VALUE: + return self.defaults.value + assert False, f"PROGRAM ERROR: bad _input_shape_template assignment for '{self.name}'." + + def get_input_shape(self, context=None): + if self._input_shape_template == VARIABLE: + return self.get_input_variables(context) + elif self._input_shape_template == VALUE: + return self.get_input_values(context) + assert False, f"PROGRAM ERROR: bad _input_shape_template assignment for '{self.name}'." + @property def position_in_mechanism(self): if hasattr(self, "owner"): diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 8611fda7ee4..4028d075254 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -1165,8 +1165,8 @@ `InputPorts ` that receive external input for that Node. These are listed in its ``external_input_ports`` (`here ` if it is Mechanism, or `here ` if it is a Composition). More specifically, the shape of the input value must be compatible with the shape of the Node's -`external_input_values` attribute (`here ` if it is Mechanism, or `here -` if it is a Composition). While these are always 2d arrays, the number and size +`external_input_variables` attribute (`here ` if it is Mechanism, or `here +` if it is a Composition). While these are always 2d arrays, the number and size of the items (corresponding to each InputPort) may vary; in some case shorthand notations are allowed, as illustrated in the `examples ` below. @@ -2021,9 +2021,14 @@ def input_function(env, result): therefore, the input specified for each `TRIAL ` must be two length 1 arrays. See `figure ` for an illustration of the format for an input dictionary. +COMMENT: MODIFIED 2/4/22 OLD: +# FIX: 2/4/22 - ADD NOTE THAT external_input_values IS NOT NECESSARILY SAME AS external_input_variables + AS SOME InputPorts CAN HAVE FUNCTIONS THAT CHANGE THE SHAPE OF variable->value (e.g., Reduce) + # Furthermore, Mechanisms can also have InputPorts with a `function ` that changes + # the size of its input when generatings its `value `, in which case its `e .. note:: A `Node's ` `external_input_values` attribute is always a 2d list in which the index i - element is the value of the i'th element of the Node's `external_input_ports` attribute. For Mechanisms, + element is the variable of the i'th element of the Node's `external_input_ports` attribute. For Mechanisms, the `external_input_values ` is often the same as its `variable `. However, some Mechanisms may have InputPorts marked as `internal_only ` which are excluded from its `external_input_ports ` @@ -2031,6 +2036,18 @@ def input_function(env, result): input value. The same considerations extend to the `external_input_ports ` and `external_input_values ` of a Composition, based on the Mechanisms and/or `nested Compositions ` that comprise its `INPUT` Nodes. +MODIFIED 2/4/22 NEW: +COMMENT +.. note:: + A `Node's ` `external_input_variables` attribute is always a 2d list in which the index i + element is the variable of the i'th element of the Node's `external_input_ports` attribute. For Mechanisms, + the `external_input_variables ` is often the same as its `variable + `. However, some Mechanisms may have InputPorts marked as `internal_only + ` which are excluded from its `external_input_ports ` + and therefore its `external_input_variables `, and so should not receive + an input value. The same considerations extend to the `external_input_ports ` + and `external_input_variabels ` of a Composition, based on the Mechanisms + and/or `nested Compositions ` that comprise its `INPUT` Nodes. If num_trials is not in use, the number of inputs provided determines the number of `TRIAL `\\s in the run. For example, if five inputs are provided for each `INPUT` `Node `, and num_trials is not @@ -2596,8 +2613,7 @@ def input_function(env, result): from psyneulink.core.components.functions.nonstateful.transferfunctions import Identity from psyneulink.core.components.mechanisms.mechanism import Mechanism_Base, MechanismError, MechanismList from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import ControlMechanism -from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP, \ - RANDOMIZATION_CONTROL_SIGNAL, NUM_ESTIMATES, STATE_FEATURES +from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP from psyneulink.core.components.mechanisms.modulatory.learning.learningmechanism import \ LearningMechanism, ACTIVATION_INPUT_INDEX, ACTIVATION_OUTPUT_INDEX, ERROR_SIGNAL, ERROR_SIGNAL_INDEX from psyneulink.core.components.mechanisms.modulatory.modulatorymechanism import ModulatoryMechanism_Base @@ -4839,7 +4855,11 @@ def _create_CIM_ports(self, context=None): if input_port not in set(self.input_CIM_ports.keys()): # instantiate the input port on the input CIM to correspond to the node's input port interface_input_port = InputPort(owner=self.input_CIM, - variable=input_port.defaults.value, + # # MODIFIED 2/3/22 OLD: + # variable=input_port.defaults.value, + # MODIFIED 2/3/22 NEW: + variable=np.atleast_2d(input_port.defaults.variable)[0], + # MODIFIED 2/3/22 END reference_value=input_port.defaults.value, name= INPUT_CIM_NAME + "_" + node.name + "_" + input_port.name, context=context) @@ -5076,7 +5096,7 @@ def _create_CIM_ports(self, context=None): context_string = context.string new_default_variable = [ - deepcopy(input_port.defaults.value) + deepcopy(input_port.default_input_shape) for input_port in cim.input_ports ] @@ -8062,7 +8082,7 @@ def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): for j in range(len(controller.input_ports) - shadow_inputs_start_index): input_port = controller.input_ports[j + shadow_inputs_start_index] if no_predicted_input: - predicted_input = input_port.defaults.value + predicted_input = input_port.default_input_shape else: predicted_input = predicted_inputs[j] @@ -8503,10 +8523,8 @@ def _validate_single_input(self, node, input): """ # Validate that a single input is properly formatted for a node. _input = [] - node_variable = [input_port.defaults.value for input_port in node.input_ports - if not input_port.internal_only or input_port.default_input] + node_variable = node.external_input_shape match_type = self._input_matches_variable(input, node_variable) - # match_type = self._input_matches_variable(input, node_variable) if match_type == 'homogeneous': # np.atleast_2d will catch any single-input ports specified without an outer list _input = convert_to_np_array(input, 2) @@ -8546,16 +8564,16 @@ def _validate_input_shapes(self, inputs): if node_input is not None: node_input = [node_input] else: - # if node_input is None, it means there are multiple trials of input in the stimulus set, so loop - # through and validate each individual input + # if node_input is None, it may mean there are multiple trials of input in the stimulus set, + # so loop through and validate each individual input node_input = [self._validate_single_input(node, single_trial_input) for single_trial_input in stimulus] if True in [i is None for i in node_input]: - incompatible_stimulus = stimulus[node_input.index(None)] + # incompatible_stimulus = [stimulus[node_input.index(None)]] + incompatible_stimulus = np.atleast_1d(stimulus[node_input.index(None)]) + correct_stimulus = np.atleast_1d(node.external_input_shape[node_input.index(None)]) node_name = node.name - node_variable = [input_port.defaults.value for input_port in node.input_ports - if not input_port.internal_only] err_msg = f"Input stimulus ({incompatible_stimulus}) for {node_name} is incompatible with " \ - f"its external_input_values ({node_variable})." + f"the shape of its external input ({correct_stimulus})." # 8/3/17 CW: I admit the error message implementation here is very hacky; # but it's at least not a hack for "functionality" but rather a hack for user clarity if "KWTA" in str(type(node)): @@ -8749,7 +8767,7 @@ def _instantiate_input_dict(self, inputs): # If any INPUT Nodes of the Composition are not specified, add and assign default_external_input_values for node in input_nodes: if node not in inputs: - inputs[node] = node.default_external_input_values + inputs[node] = node.external_input_shape return inputs def _parse_run_inputs(self, inputs, context=None): @@ -8839,7 +8857,7 @@ def _validate_execution_inputs(self, inputs): inp = self._validate_single_input(node, inp) if inp is None: raise CompositionError(f"Input stimulus ({inp}) for {node.name} is incompatible " - f"with its variable ({node.default_external_input_values}).") + f"with its variable ({node.external_input_shape}).") _inputs[node] = inp return _inputs @@ -10475,9 +10493,12 @@ def __call__(self, *args, **kwargs): def get_inputs_format(self, **kwargs): return self.get_input_format(**kwargs, alias="get_inputs_format") - def get_input_format(self, num_trials:int=1, + def get_input_format(self, + num_trials:int=1, use_labels:bool=False, show_nested_input_nodes:bool=False, + template:bool=False, + use_names:bool=False, alias:str=None): """Return str with format of dict used by **inputs** argument of `run ` method. @@ -10488,17 +10509,33 @@ def get_input_format(self, num_trials:int=1, specifies number of trials' worth of inputs to included in format. use_labels : bool : default False - if True, shows labels instead of values for Mechanisms that have an `input_label_dict + if True, show labels instead of values for Mechanisms that have an `input_label_dict `. For **num_trials** = 1, a representative label is - shown; for **num_trials** > 1, a different label is used for each trial shown, cycling + shown; for **num_trials** > 1, use a different label for each trial shown, cycling through the set if **num_trials** is greater than the number of labels. show_nested_input_nodes : bool : default False - shows hierarchical display of `Nodes ` in `nested Compositions ` + show hierarchical display of `Nodes ` in `nested Compositions ` with names of destination `INPUT ` `Nodes ` and representative inputs, followed by the actual format used for the `run ` method. + + template : bool : default False + return dict (with **num_trials** worth of default values for each `INPUT ` `Node + `) properly formatted for use inputs arg of `run ` method. + + use_names : bool : default False + use `Node ` name as key for Node in template dict. """ + if template: + # Return dict that can be used as inputs arg to run() + input_dict = {} + for node in self.get_nodes_by_role(NodeRole.INPUT): + node_key = node.name if use_names else node + inputs_for_node = [port.default_input_shape for port in node.external_input_ports] + input_dict[node_key]=[inputs_for_node] * num_trials + return input_dict + if alias: warnings.warn(f"{alias} is aliased to get_input_format(); please use that in the future.") @@ -10518,6 +10555,9 @@ def _get_inputs(comp, nesting_level=1, use_labels=False): trials = [] for t in range(num_trials): + # FIX: 2/3/22 - SHOULD REFACTOR TO USE InputPort.variable RATHER THAN input_values + # IN CASE AN InputPort'S FUNCTION CHANGES ITS SHAPE + # (SEE ABOVE FOR template) # Mechanism with labels if use_labels and isinstance(node, Mechanism) and node.input_labels_dict: input_values = [] @@ -11178,9 +11218,6 @@ def runs_simulations(self): def simulation_results(self): return self.parameters.simulation_results.get(self.default_execution_id) - # For now, external_input_ports == input_ports and external_input_values == input_values - # They could be different in the future depending on new state_features (ex. if we introduce recurrent compositions) - # Useful to have this property for treating Compositions the same as Mechanisms in run & execute @property def external_input_ports(self): """Returns all external InputPorts that belong to the Input CompositionInterfaceMechanism""" @@ -11189,18 +11226,52 @@ def external_input_ports(self): except (TypeError, AttributeError): return None + @property + def external_input_shape(self): + """Alias for _default_external_input_shape""" + return self._default_external_input_shape + + @property + def _default_external_input_shape(self): + """Returns default_input_shape of all external InputPorts that belong to Input CompositionInterfaceMechanism""" + try: + return [input_port.default_input_shape for input_port in self.input_CIM.input_ports + # FIX: 2/4/22 - IS THIS NEEDED (HERE OR BELOW -- DO input_CIM.input_ports EVER GET ASSIGNED THIS? + if not input_port.internal_only] + except (TypeError, AttributeError): + return None + + @property + def external_input_variables(self): + """Return variables of all external InputPorts that belong to the Input CompositionInterfaceMechanism""" + try: + return [input_port.variable for input_port in self.input_CIM.input_ports if not input_port.internal_only] + except (TypeError, AttributeError): + return None + + @property + def default_external_input_variables(self): + """Return default variables of all external InputPorts that belong to the Input CompositionInterfaceMechanism + """ + + try: + return [input_port.defaults.variable for input_port in self.input_CIM.input_ports if + not input_port.internal_only] + except (TypeError, AttributeError): + return None + @property def external_input_values(self): - """Returns values of all external InputPorts that belong to the Input CompositionInterfaceMechanism""" + """Return values of all external InputPorts that belong to the Input CompositionInterfaceMechanism""" try: + # FIX: 2/4/22 SHOULD input_port.variable REPLACE input_port.value HERE? return [input_port.value for input_port in self.input_CIM.input_ports if not input_port.internal_only] except (TypeError, AttributeError): return None @property def default_external_input_values(self): - """Return the default values of all external InputPorts that belong to the - Input CompositionInterfaceMechanism + """Return the default values of all external InputPorts that belong to the Input CompositionInterfaceMechanism """ try: diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index 6702cccb373..ca37018a3a7 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -802,9 +802,7 @@ def __init__(self, FUNCTION: lambda v: [float(v[2][0][0]), 0] \ if (v[1] - v[0]) < (v[1] + v[0]) \ else [0, float(v[2][0][1])] - } - ]) # Add StandardOutputPorts for Mechanism (after ones for DDM, so that their indices are not messed up) @@ -1028,7 +1026,7 @@ def _execute( :rtype self.outputPort.value: (number) """ - if variable is None or np.isnan(variable): + if variable is None or any(np.isnan(i) for i in variable): # IMPLEMENT: MULTIPROCESS DDM: ??NEED TO DEAL WITH PARTIAL NANS variable = self.defaults.variable diff --git a/psyneulink/library/components/mechanisms/processing/transfer/kwtamechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/kwtamechanism.py index d534b2b9b68..12c1369996e 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/kwtamechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/kwtamechanism.py @@ -402,13 +402,6 @@ def __init__(self, ) def _parse_function_variable(self, variable, context=None): - if variable.dtype.char == "U": - raise KWTAError( - "input ({0}) to {1} was a string, which is not supported for {2}".format( - variable, self, self.__class__.__name__ - ) - ) - return self._kwta_scale(variable, context=context) # adds indexOfInhibitionInputPort to the attributes of KWTAMechanism diff --git a/tests/components/test_component.py b/tests/components/test_component.py index bf6a8db16c0..54023ee3304 100644 --- a/tests/components/test_component.py +++ b/tests/components/test_component.py @@ -20,15 +20,14 @@ def test_detection_of_illegal_args_in_kwargs(self): assert "Unrecognized arguments in constructor for MY_MECH (type: ProcessingMechanism): 'flim_flam, grumblabble'" def test_component_execution_counts_for_standalone_mechanism(self): - """Note: input_port should not update execution count, since it has no afferents""" T = pnl.TransferMechanism() T.execute() assert T.execution_count == 1 - assert T.input_port.execution_count == 0 + assert T.input_port.execution_count == 1 # incremented by Mechanism.get_variable_from_input() - # skipped (0 executions) because we bypass execute when no afferents, and + # skipped (0 executions) because execution is bypassed when no afferents, and # function._is_identity is satisfied (here, Linear function with slope 0 and intercept 1) # This holds true for each below assert T.parameter_ports[pnl.SLOPE].execution_count == 0 @@ -36,13 +35,13 @@ def test_component_execution_counts_for_standalone_mechanism(self): T.execute() assert T.execution_count == 2 - assert T.input_port.execution_count == 0 + assert T.input_port.execution_count == 2 assert T.parameter_ports[pnl.SLOPE].execution_count == 0 assert T.output_port.execution_count == 0 T.execute() assert T.execution_count == 3 - assert T.input_port.execution_count == 0 + assert T.input_port.execution_count == 3 assert T.parameter_ports[pnl.SLOPE].execution_count == 0 assert T.output_port.execution_count == 0 diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 4d73cfdb42e..3333f650b55 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -2347,7 +2347,7 @@ def test_3_mechanisms_2_origins_1_disable_control_1_terminal(self, benchmark, co @pytest.mark.composition @pytest.mark.benchmark(group="Transfer") - def test_transfer_mechanism(self, benchmark, comp_mode): + def xtest_transfer_mechanism(self, benchmark, comp_mode): # mechanisms C = TransferMechanism(name="C", @@ -5746,6 +5746,39 @@ def inputs_generator_function(): else: assert ocomp.results[0:2] == ocomp.results[2:4] == ocomp.results[4:6] == [[-2], [100]] + def test_get_input_format(self): + A = ProcessingMechanism(size=1, name='A') + B = ProcessingMechanism(size=2, name='B') + C = ProcessingMechanism(size=[3,3], input_ports=['C INPUT 1', 'C INPUT 2'], name='C') + assert C.variable.shape == (2,3) + X = ProcessingMechanism(size=4, name='X') + Y = ProcessingMechanism(input_ports=[{NAME:'Y INPUT 1', pnl.SIZE: 3, pnl.FUNCTION: pnl.Reduce}], + name='Y') + assert len(Y.input_port.variable) == 3 + assert len(Y.input_port.value) == 1 + icomp = Composition(pathways=[[A,B],[C]], name='ICOMP') + ocomp = Composition(nodes=[X, icomp, Y], name='OCOMP') + + expected = '{\n\tX: [ [[0.0, 0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0, 0.0]] ],' \ + '\n\tICOMP: [ [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ],' \ + '\n\tY: [ [[0.0]], [[0.0]] ]\n}' + inputs_dict = ocomp.get_input_format(num_trials=2) + assert inputs_dict == expected + + expected = '\nInputs to (nested) INPUT Nodes of OCOMP for 1 trials:\n\tX: [[0.0, 0.0, 0.0, 0.0]]' \ + '\n\tICOMP: \n\t\tA: [[0.0]]\n\t\tC: [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]]\n\tY: [[0.0]' \ + '\n\nFormat as follows for inputs to run():\n{\n\tX: [[0.0, 0.0, 0.0, 0.0]],' \ + '\n\tICOMP: [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]],\n\tY: [[0.0]]\n}' + inputs_dict = ocomp.get_input_format(show_nested_input_nodes=True) + assert inputs_dict == expected + + inputs_dict = ocomp.get_input_format(template=True, use_names=False, num_trials=2) + ocomp.run(inputs=inputs_dict) + len(ocomp.results)==2 + + inputs_dict = ocomp.get_input_format(template=True, use_names=True, num_trials=2) + ocomp.run(inputs=inputs_dict) + len(ocomp.results)==2 class TestProperties: @pytest.mark.composition diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 70ef2009e0c..b22e2a689a6 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -800,12 +800,12 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c f'that are missing from \'OUTER COMP\' and any Compositions nested within it."', # 4 - f"The '{pnl.STATE_FEATURES}' argument has been specified for 'OptimizationControlMechanism-0' " - f"that is using a Composition ('OUTER COMP') as its agent_rep, but they are not compatible with " - f"the inputs required by its 'agent_rep': 'Input stimulus (0.0) for OB is incompatible with its " - f"external_input_values ([array([0., 0., 0.])]).' Use the get_inputs_format() method of 'OUTER COMP' " - f"to see the required format, or remove the specification of '{pnl.STATE_FEATURES}' from the constructor " - f"for OptimizationControlMechanism-0 to have them automatically assigned.", + f"The 'state_features' argument has been specified for 'OptimizationControlMechanism-0' that is using a " + f"Composition ('OUTER COMP') as its agent_rep, but they are not compatible with the inputs required by " + f"its 'agent_rep': 'Input stimulus ([0.]) for OB is incompatible with the shape of its external input " + f"([0. 0. 0.]).' Use the get_inputs_format() method of 'OUTER COMP' to see the required format, or " + f"remove the specification of 'state_features' from the constructor for OptimizationControlMechanism-0 " + f"to have them automatically assigned.", # 5 f"The number of '{pnl.STATE_FEATURES}' specified for OptimizationControlMechanism-0 (4) is more than " diff --git a/tests/composition/test_learning.py b/tests/composition/test_learning.py index de7dae19478..a7edd129d15 100644 --- a/tests/composition/test_learning.py +++ b/tests/composition/test_learning.py @@ -274,13 +274,13 @@ def test_target_spec_over_nesting_of_items_in_target_value_error(self): comp.run(inputs={A: [1.0, 2.0, 3.0], p.target: [[[3.0], [4.0]], [[5.0], [6.0]], [[7.0], [8.0]]]}) assert ("Input stimulus" in str(error_text.value) and - "for Target is incompatible with its external_input_values" in str(error_text.value)) + "for Target is incompatible with the shape of its external input" in str(error_text.value)) # Elicit error with learn with pytest.raises(RunError) as error_text: comp.learn(inputs={A: [1.0, 2.0, 3.0], p.target: [[[3.0], [4.0]], [[5.0], [6.0]], [[7.0], [8.0]]]}) assert ("Input stimulus" in str(error_text.value) and - "for Target is incompatible with its external_input_values" in str(error_text.value)) + "for Target is incompatible with the shape of its external input" in str(error_text.value)) # The input sizes were picked because the lengths conflict in set: # >>> print({10, 2}, {2, 10}) diff --git a/tests/composition/test_report.py b/tests/composition/test_report.py index 82e79dbf443..9b23a372c27 100644 --- a/tests/composition/test_report.py +++ b/tests/composition/test_report.py @@ -174,6 +174,8 @@ def test_nested_comps_and_sims_basic(self): """Test output and progress reports for execution of simulations by controller in both an outer and nested composition, using input dictionary, generator instance and generator function. """ + # pnl.clear_registry() + pnl.clear_registry(pnl.FunctionRegistry) # instantiate mechanisms and inner comp ia = pnl.TransferMechanism(name='ia') @@ -397,7 +399,7 @@ def inputs_generator_function(): report_to_devices=ReportDevices.DIVERT ) actual_output = ocomp.rich_diverted_reports - expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp BEFORE its Trial 18 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [54.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 18 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 34.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-20.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -20.0 │ │ ┃ ║\n ║ ┃ │ │ output: 34.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-20.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp AFTER its Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [34.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp BEFORE its Trial 19 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [34.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 19 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 1.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 44.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: 10.0 │ │ ┃ ║\n ║ ┃ │ │ output: 44.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp AFTER its Trial 1 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[1.0]] ┃ ║\n ║ ┃ outcome: [44.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nocomp: Executed 2 of 2 trials\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 1)\n' + expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp BEFORE its Trial 18 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [54.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 18 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 34.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-20.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -20.0 │ │ ┃ ║\n ║ ┃ │ │ output: 34.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-20.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp AFTER its Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [34.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp BEFORE its Trial 19 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [34.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 19 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 1.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 44.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: 10.0 │ │ ┃ ║\n ║ ┃ │ │ output: 44.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp AFTER its Trial 1 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[1.0]] ┃ ║\n ║ ┃ outcome: [44.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nocomp: Executed 2 of 2 trials\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 1)\n' assert actual_output == expected_output ocomp.run(inputs=inputs_dict, @@ -407,7 +409,7 @@ def inputs_generator_function(): report_to_devices=ReportDevices.DIVERT ) actual_output = ocomp.rich_diverted_reports - expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp BEFORE its Trial 20 ━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [44.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 42.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -11. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 33.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-11.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 24.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 20 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 24.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-20.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -20.0 │ │ ┃ ║\n ║ ┃ │ │ output: 24.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-20.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━ oController SIMULATION OF ocomp AFTER its Trial 0 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [24.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp BEFORE its Trial━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [24.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp BEFORE its Trial━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [24.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp BEFORE its Trial 21 ━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [24.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 1. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 1.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 25.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 5.5 (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 5.5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 29.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[5.5]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 34.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[10.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 21 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 1.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 34.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: 10.0 │ │ ┃ ║\n ║ ┃ │ │ output: 34.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━ oController SIMULATION OF ocomp AFTER its Trial 1 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[1.0]] ┃ ║\n ║ ┃ outcome: [34.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp BEFORE its Trial━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [24.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp BEFORE its Trial━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [24.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nocomp: Executed 2 of 2 trials\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n' + expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp BEFORE its Trial 20 ━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [44.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 42.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -11. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 33.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-11.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 24.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 20 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 24.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-20.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -20.0 │ │ ┃ ║\n ║ ┃ │ │ output: 24.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-20.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━ oController SIMULATION OF ocomp AFTER its Trial 0 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [24.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp BEFORE its Trial━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [24.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp BEFORE its Trial━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [24.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp BEFORE its Trial 21 ━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [24.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 1. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 1.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 25.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 5.5 (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 5.5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 29.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[5.5]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 34.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[10.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 21 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 1.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 34.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: 10.0 │ │ ┃ ║\n ║ ┃ │ │ output: 34.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━ oController SIMULATION OF ocomp AFTER its Trial 1 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[1.0]] ┃ ║\n ║ ┃ outcome: [34.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp BEFORE its Trial━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [24.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp BEFORE its Trial━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [24.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nocomp: Executed 2 of 2 trials\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n' assert actual_output == expected_output icomp.controller_mode = pnl.AFTER @@ -420,7 +422,7 @@ def inputs_generator_function(): report_to_devices=ReportDevices.DIVERT ) actual_output = ocomp.rich_diverted_reports - expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [34.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 22 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 14.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-20.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp AFTER its Trial 22 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [14.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -20.0 │ │ ┃ ║\n ║ ┃ │ │ output: 14.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-20.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp BEFORE its Trial 1 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[1.0]] ┃ ║\n ║ ┃ outcome: [14.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 23 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 1.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 24.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp AFTER its Trial 23 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [24.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: 10.0 │ │ ┃ ║\n ║ ┃ │ │ output: 24.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nocomp: Executed 2 of 2 trials\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 1)\n' + expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [34.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 22 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 14.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-20.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp AFTER its Trial 22 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [14.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -20.0 │ │ ┃ ║\n ║ ┃ │ │ output: 14.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-20.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp BEFORE its Trial 1 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[1.0]] ┃ ║\n ║ ┃ outcome: [14.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 23 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 1.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 24.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp AFTER its Trial 23 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [24.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: 10.0 │ │ ┃ ║\n ║ ┃ │ │ output: 24.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nocomp: Executed 2 of 2 trials\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 1)\n' assert actual_output == expected_output ocomp.run(inputs=inputs_dict, @@ -430,11 +432,13 @@ def inputs_generator_function(): report_to_devices=ReportDevices.DIVERT ) actual_output = ocomp.rich_diverted_reports - expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [24.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [4.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -7.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -16.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -200. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController Objective Mechanism and │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ oController Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -200.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -176.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-200.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-176.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -178.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -187.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -196.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -176.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-200.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 24 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 4.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-20.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━ iController SIMULATION OF icomp AFTER its Trial 24 ━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [4.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -11. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -7.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-11.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -16.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -20.0 │ │ ┃ ║\n ║ ┃ │ │ output: 4.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-20.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━ oController SIMULATION OF ocomp BEFORE its Trial 1 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[1.0]] ┃ ║\n ║ ┃ outcome: [4.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [4.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -7.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -16.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -200. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController Objective Mechanism and │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ oController Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -200.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -176.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-200.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-176.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -178.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -187.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -196.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -176.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-200.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 25 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 1.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 14.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━ iController SIMULATION OF icomp AFTER its Trial 25 ━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [14.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 1. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 1.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 15.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 5.5 (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 5.5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 19.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[5.5]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 24.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[10.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: 10.0 │ │ ┃ ║\n ║ ┃ │ │ output: 14.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nocomp: Executed 2 of 2 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n' + expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [24.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [4.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -7.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -16.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -200. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController Objective Mechanism and │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ oController Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -200.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -176.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-200.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-176.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -178.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -187.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -196.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -176.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-200.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 24 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -20.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 4.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-20.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━ iController SIMULATION OF icomp AFTER its Trial 24 ━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [4.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -11. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -7.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-11.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -16.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -20.0 │ │ ┃ ║\n ║ ┃ │ │ output: 4.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-20.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━ oController SIMULATION OF ocomp BEFORE its Trial 1 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[1.0]] ┃ ║\n ║ ┃ outcome: [4.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [4.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: 2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -7.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -16.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: 4.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -200. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController Objective Mechanism and │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ oController Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -200.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -176.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-200.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-176.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -178.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -187.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-11.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -196.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -200.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -176.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-200.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 25 ━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 1.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: 10.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: 14.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━ iController SIMULATION OF icomp AFTER its Trial 25 ━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [14.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 1. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 1.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 15.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 5.5 (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 5.5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 5.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 19.5 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[5.5]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 2: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[1.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 1.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰─────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └─────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: 10. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: 10.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: 10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: 24.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[10.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[10.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: 10.0 │ │ ┃ ║\n ║ ┃ │ │ output: 14.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[10.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nocomp: Executed 2 of 2 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trial (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n' assert actual_output == expected_output def test_nested_comps_and_sims_with_modulated_and_monitored_params_and_use_prefs(self): + pnl.clear_registry() + # instantiate mechanisms and inner comp ia = pnl.TransferMechanism(name='ia') ib = pnl.TransferMechanism(name='ib') @@ -473,8 +477,6 @@ def test_nested_comps_and_sims_with_modulated_and_monitored_params_and_use_prefs # setup structure of outer comp ocomp.add_node(icomp) - # ocomp._analyze_graph() - # add controller to outer comp ocomp.add_controller( pnl.OptimizationControlMechanism( @@ -496,8 +498,6 @@ def test_nested_comps_and_sims_with_modulated_and_monitored_params_and_use_prefs num=3))]) ) - # inputs_dict = {icomp:{ia: [[-2], [1]]}} - ocomp.run(inputs={icomp:-2}, report_output=ReportOutput.FULL, report_params=[pnl.ReportParams.MODULATED, pnl.ReportParams.MONITORED], @@ -507,7 +507,7 @@ def test_nested_comps_and_sims_with_modulated_and_monitored_params_and_use_prefs # report_to_devices=ReportDevices.RECORD ) actual_output = ocomp.rich_diverted_reports - expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [0.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-2.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -4. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -22. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ─────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -2. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -11. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-11.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-11.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -13. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -31. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -31.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌───────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ──────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭──────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -11. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰────────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰────────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-11.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 2: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-20.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -22. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -40. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -40.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌───────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ──────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭──────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -20. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰────────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰────────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 0 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ia ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ slope: 1.0 (modulated by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ and oController) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -2. (monitored by iController) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━ iController SIMULATION OF icomp AFTER its Trial 0 ━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [-2.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -4. (monitored by iController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -4.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -22. (monitored by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -22.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── oController Objective Mechanism ─────╮ │ ┃ ║\n ║ ┃ │ │ input: -2.0 │ │ ┃ ║\n ║ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ║\n ║ ┃ │ │ │ value: -2. (monitored by oController) │ │ │ ┃ ║\n ║ ┃ │ │ │ │ │ │ ┃ ║\n ║ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ║\n ║ ┃ │ │ output: -2.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\n' + expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [0.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-2.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -4. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -4.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -22. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ─────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -2. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -11. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -11. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-11.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-11.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -13. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -13.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -31. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -31.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌───────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ──────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭──────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -11. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰────────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰────────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-11.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 2: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-20.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -22. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -22.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -40. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -40.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌───────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ──────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭──────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -20. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰────────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰────────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 0 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ia ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ slope: 1.0 (modulated by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ and oController) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -2. (monitored by iController) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━ iController SIMULATION OF icomp AFTER its Trial 0 ━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [-2.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -4. (monitored by iController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -4.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -22. (monitored by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -22.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── oController Objective Mechanism ─────╮ │ ┃ ║\n ║ ┃ │ │ input: -2.0 │ │ ┃ ║\n ║ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ║\n ║ ┃ │ │ │ value: -2. (monitored by oController) │ │ │ ┃ ║\n ║ ┃ │ │ │ │ │ │ ┃ ║\n ║ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ║\n ║ ┃ │ │ output: -2.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\n' assert actual_output == expected_output ocomp.run(inputs={icomp:-2}, @@ -519,7 +519,7 @@ def test_nested_comps_and_sims_with_modulated_and_monitored_params_and_use_prefs # report_to_devices=ReportDevices.RECORD ) actual_output = ocomp.rich_diverted_reports - expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [-2.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -4.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp AFTER its Trial 1 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [-4.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -2.0 │ │ ┃ ║\n ║ ┃ │ │ output: -4.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\n' + expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [-2.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 1 ━━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────── Time Step 0 ────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────── ia ───────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────── params ───────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰─────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰─────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └─────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────── Time Step 2 ─────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭─ iController Objective Mechanism ─╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -4.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━ iController SIMULATION OF icomp AFTER its Trial 1 ━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [-4.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────── Time Step 1 ─────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─ oController Objective Mechanism ─╮ │ ┃ ║\n ║ ┃ │ │ input: -2.0 │ │ ┃ ║\n ║ ┃ │ │ output: -4.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\n' assert actual_output == expected_output ocomp.run(inputs={icomp:-2}, @@ -546,7 +546,7 @@ def test_nested_comps_and_sims_with_modulated_and_monitored_params_and_use_prefs # report_to_devices=ReportDevices.RECORD ) actual_output = ocomp.rich_diverted_reports - expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [-6.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -8. (monitored by iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -8.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-8.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -10. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -10.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -28. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -28.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ─────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -8. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -8.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -11. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -17. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -17.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-11.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-17.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -19. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -19.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -37. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -37.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌───────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ──────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭──────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -17. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰────────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -17.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰────────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-11.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 2: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -26. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -26.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-26.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -28. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -28.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -46. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -46.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌───────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ──────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭──────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -26. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰────────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -26.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰────────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 3 ━━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 0 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ia ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ slope: 1.0 (modulated by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ and oController) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -8. (monitored by iController) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -8.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━ iController SIMULATION OF icomp AFTER its Trial 3 ━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [-8.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -10. (monitored by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -28. (monitored by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -28.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── oController Objective Mechanism ─────╮ │ ┃ ║\n ║ ┃ │ │ input: -2.0 │ │ ┃ ║\n ║ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ║\n ║ ┃ │ │ │ value: -8. (monitored by oController) │ │ │ ┃ ║\n ║ ┃ │ │ │ │ │ │ ┃ ║\n ║ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ║\n ║ ┃ │ │ output: -8.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\n' + expected_output = '\n ╔════════════════════════════ EXECUTION OF ocomp ════════════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━ oController SIMULATION OF ocomp BEFORE its Trial 0 ━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ state: [[-2.0]] ┃ ║\n ║ ┃ outcome: [-6.0] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 0: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -8. (monitored by iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -8.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-8.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -10. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -10.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -28. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -28.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ─────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -8. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -8.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 1: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -11. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -11.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -17. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -17.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-11.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-17.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -19. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -19.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -37. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -37.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌───────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ──────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -11.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭──────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -17. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰────────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -17.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰────────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-11.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┏━━━━━━━━━━━━━━━━━━ ocomp SIMULATION 2: Trial 0 ━━━━━━━━━━━━━━━━━━┓ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌──────────────────────── Time Step 0 ────────────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┏━━━━━━━━━━━━ EXECUTION OF icomp within ocomp ━━━━━━━━━━━━┓ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━━━━━━━━━━━━━━━━━ icomp: Trial 0 ━━━━━━━━━━━━━━━━━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ input: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ value: -26. (monitored by │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ │ │ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ output: -26.0 │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ result: [[-20.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┏━ iController SIMULATION OF icomp AFTER its Trial ━┓ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ state: [[-2.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ outcome: [-26.0] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -2. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -28. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -28.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┏━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━┓ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ input: [[-2.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 0 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ia ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 1 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭───────────────── ib ─────────────────╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -20. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController Objective Mechanism │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ and oController Objective │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ Mechanism) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┌────────────── Time Step 2 ──────────────┐ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╭── iController Objective Mechanism ───╮ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╭───────────── params ─────────────╮ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ value: -46. (monitored by │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ╰──────────────────────────────────╯ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ output: -46.0 │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ ╰──────────────────────────────────────╯ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ │ │ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ └──────────────────────────────────────────┘ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ result: [[-20.0]] ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ control allocation: [[1.0]] ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┃ ┃ │ ┃ ┃ ║\n ║ ┃ ┃ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └──────────────────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ ┌───────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╭───── oController Objective Mechanism ──────╮ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╭──────────────── params ────────────────╮ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ value: -26. (monitored by oController) │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ╰────────────────────────────────────────╯ │ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ output: -26.0 │ │ ┃ ┃ ║\n ║ ┃ ┃ │ ╰────────────────────────────────────────────╯ │ ┃ ┃ ║\n ║ ┃ ┃ │ │ ┃ ┃ ║\n ║ ┃ ┃ └────────────────────────────────────────────────┘ ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║\n ║ ┃ ┃ ┃ ┃ ║\n ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ control allocation: [[1.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━ ocomp: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 0 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╔══════════════ EXECUTION OF icomp within ocomp ═══════════════╗ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━━━━━━━━━━━━━━━━━━ icomp: Trial 3 ━━━━━━━━━━━━━━━━━━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ input: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 0 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ia ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ slope: 1.0 (modulated by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ and oController) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌─────────────────── Time Step 1 ───────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭────────────────────── ib ──────────────────────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭────────────────── params ──────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰────────────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰────────────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └────────────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ input: -2.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ value: -8. (monitored by iController) │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ │ │ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ output: -8.0 │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ │ │ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ └───────────────────────────────────────────────┘ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ result: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ║ ┏━━ iController SIMULATION OF icomp AFTER its Trial 3 ━━━┓ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ state: [[-2.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ outcome: [-8.0] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 0: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -2. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -2.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -10. (monitored by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -10.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┏━━━━━━━━━━ icomp SIMULATION 1: Trial 0 ━━━━━━━━━━━┓ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ input: [[-2.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 0 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ia ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -2.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-5 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ intercept: 0.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ slope: 1.0 (modulated by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController and oController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭─────────────────── ib ────────────────────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -20. (monitored by iController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism and oController │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ Objective Mechanism) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ function: Linear Function-14 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ variable: -20.0 │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┌──────────────── Time Step 2 ─────────────────┐ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╭───── iController Objective Mechanism ─────╮ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ input: -20.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ value: -28. (monitored by │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ iController) │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ │ │ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ output: -28.0 │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ │ │ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ └───────────────────────────────────────────────┘ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ result: [[-20.0]] ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ control allocation: [[1.0]] ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┃ ┃ ║ │ ┃ ║\n ║ ┃ │ ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║ │ ┃ ║\n ║ ┃ │ ║ ║ │ ┃ ║\n ║ ┃ │ ╚══════════════════════════════════════════════════════════════╝ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──────────────── Time Step 1 ─────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── oController Objective Mechanism ─────╮ │ ┃ ║\n ║ ┃ │ │ input: -2.0 │ │ ┃ ║\n ║ ┃ │ │ ╭─────────────── params ────────────────╮ │ │ ┃ ║\n ║ ┃ │ │ │ value: -8. (monitored by oController) │ │ │ ┃ ║\n ║ ┃ │ │ │ │ │ │ ┃ ║\n ║ ┃ │ │ ╰───────────────────────────────────────╯ │ │ ┃ ║\n ║ ┃ │ │ output: -8.0 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[-2.0]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\n' assert actual_output == expected_output icomp.controller.reportOutputPref = ReportOutput.ON @@ -555,6 +555,7 @@ def test_nested_comps_and_sims_with_modulated_and_monitored_params_and_use_prefs @pytest.mark.pytorch def test_autodiff_report(self): + xor_in = pnl.TransferMechanism(name='xor_in', default_variable=np.zeros(2)) @@ -626,7 +627,7 @@ def test_autodiff_report(self): report_progress=ReportProgress.ON, report_to_devices=ReportDevices.DIVERT) actual_report = xor.rich_diverted_reports - expected_report = '\n ╔══ EXECUTION OF autodiff_composition ═══╗\n ║ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933057795354014]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 1 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999331787548446]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 2 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317875516309]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 3 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998504229552773]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 4 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933055512239266]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 5 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317539824547]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 6 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317539856401]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 7 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998504138991838]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 8 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933053228968025]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 9 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317204136144]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 10 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317204168003]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 11 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999850404842228]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════╝\n\nautodiff_composition: Trained 12 trials\n' + expected_report = '\n ╔══ EXECUTION OF autodiff_composition ═══╗\n ║ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933057795354014]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 1 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999331787548446]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 2 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317875516309]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 3 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998504229552773]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 4 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933055512239266]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 5 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317539824547]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 6 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317539856401]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 7 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998504138991838]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 8 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933053228968025]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 9 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317204136144]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 10 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317204168003]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 11 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999850404842228]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════╝\n\nautodiff_composition: Trained 12 trials\n' assert actual_report == expected_report xor.run(inputs={xor_in:xor_inputs}, @@ -635,7 +636,7 @@ def test_autodiff_report(self): report_to_devices=ReportDevices.DIVERT ) actual_report = xor.rich_diverted_reports - expected_report = '\n ╔════════════════════ EXECUTION OF autodiff_composition ═════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━ autodiff_composition: Trial 0 ━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[0, 0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.0 0.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌─────────────────── Time Step 1 ────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭──────────────────── xor_hid ────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │ │ ┃ ║\n ║ ┃ │ ╰─────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └─────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 5.0 │ │ ┃ ║\n ║ ┃ │ │ output: 0.993 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933050945540283]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 1 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[0, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 1.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.0 1.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0.99999972 0.99999972 0.99999972 0.99999972 │ │ ┃ ║\n ║ ┃ │ │ 0.99999972 0.99999972 │ │ ┃ ║\n ║ ┃ │ │ 0.99999972 0.99999972 0.99999972 0.99999972]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 │ │ ┃ ║\n ║ ┃ │ │ 0.731 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 7.31 │ │ ┃ ║\n ║ ┃ │ │ output: 0.999 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317190927715]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 2 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 0.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0.99999972 0.99999972 0.99999972 0.99999972 │ │ ┃ ║\n ║ ┃ │ │ 0.99999972 0.99999972 │ │ ┃ ║\n ║ ┃ │ │ 0.99999972 0.99999972 0.99999972 0.99999972]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 │ │ ┃ ║\n ║ ┃ │ │ 0.731 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 7.31 │ │ ┃ ║\n ║ ┃ │ │ output: 0.999 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317190927715]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 3 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1. 1.]] │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 1.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1.99999944 1.99999944 1.99999944 1.99999944 │ │ ┃ ║\n ║ ┃ │ │ 1.99999944 1.99999944 │ │ ┃ ║\n ║ ┃ │ │ 1.99999944 1.99999944 1.99999944 1.99999944]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.881 0.881 0.881 0.881 0.881 0.881 0.881 0.881 0.881 │ │ ┃ ║\n ║ ┃ │ │ 0.881 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌─ Time Step 2 ──┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭── xor_out ──╮ │ ┃ ║\n ║ ┃ │ │ input: 8.81 │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 │ │ ┃ ║\n ║ ┃ │ ╰─────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └─────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998504044852918]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nautodiff_composition: Executed 4 of 4 trials\n' + expected_report = '\n ╔════════════════════ EXECUTION OF autodiff_composition ═════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━ autodiff_composition: Trial 0 ━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[0.0, 0.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.0 0.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌─────────────────── Time Step 1 ────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭──────────────────── xor_hid ────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │ │ ┃ ║\n ║ ┃ │ ╰─────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └─────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 5.0 │ │ ┃ ║\n ║ ┃ │ │ output: 0.993 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933050945540283]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 1 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[0.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 1.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.0 1.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0.99999972 0.99999972 0.99999972 0.99999972 │ │ ┃ ║\n ║ ┃ │ │ 0.99999972 0.99999972 │ │ ┃ ║\n ║ ┃ │ │ 0.99999972 0.99999972 0.99999972 0.99999972]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 │ │ ┃ ║\n ║ ┃ │ │ 0.731 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 7.31 │ │ ┃ ║\n ║ ┃ │ │ output: 0.999 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317190927715]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 2 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 0.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 0.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0.99999972 0.99999972 0.99999972 0.99999972 │ │ ┃ ║\n ║ ┃ │ │ 0.99999972 0.99999972 │ │ ┃ ║\n ║ ┃ │ │ 0.99999972 0.99999972 0.99999972 0.99999972]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 │ │ ┃ ║\n ║ ┃ │ │ 0.731 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 7.31 │ │ ┃ ║\n ║ ┃ │ │ output: 0.999 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993317190927715]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 3 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1. 1.]] │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 1.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1.99999944 1.99999944 1.99999944 1.99999944 │ │ ┃ ║\n ║ ┃ │ │ 1.99999944 1.99999944 │ │ ┃ ║\n ║ ┃ │ │ 1.99999944 1.99999944 1.99999944 1.99999944]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.881 0.881 0.881 0.881 0.881 0.881 0.881 0.881 0.881 │ │ ┃ ║\n ║ ┃ │ │ 0.881 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌─ Time Step 2 ──┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭── xor_out ──╮ │ ┃ ║\n ║ ┃ │ │ input: 8.81 │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 │ │ ┃ ║\n ║ ┃ │ ╰─────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └─────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998504044852918]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\nautodiff_composition: Executed 4 of 4 trials\n' assert actual_report == expected_report xor.learn(inputs= training_inputs, @@ -660,7 +661,7 @@ def test_autodiff_report(self): report_progress=ReportProgress.OFF, report_to_devices=ReportDevices.DIVERT) actual_report = xor.rich_diverted_reports - expected_report = '\n ╔══ EXECUTION OF autodiff_composition ═══╗\n ║ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933044094317858]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 1 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315861097587]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 2 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315861129465]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 3 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998503686057807]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 4 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933041810263933]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 5 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999331552526669]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 6 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315525298574]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 7 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998503595445122]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 8 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933039526053421]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 9 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315189407287]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 10 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315189439175]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 11 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998503504823807]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════╝\n\n' + expected_report = '\n ╔══ EXECUTION OF autodiff_composition ═══╗\n ║ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 0 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933044094317858]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 1 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315861097587]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 2 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315861129465]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 3 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998503686057807]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 4 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933041810263933]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 5 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999331552526669]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 6 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315525298574]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 7 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998503595445122]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 8 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933039526053421]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 9 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315189407287]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 10 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9993315189439175]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━ autodiff_composition: Trial 11 ━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998503504823807]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════╝\n\n' assert actual_report == expected_report xor.run(inputs={xor_in:xor_inputs}, @@ -669,5 +670,5 @@ def test_autodiff_report(self): report_to_devices=ReportDevices.DIVERT ) actual_report = xor.rich_diverted_reports - expected_report = '\n ╔════════════════════ EXECUTION OF autodiff_composition ═════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━ autodiff_composition: Trial 0 ━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[0, 0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.0 0.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌─────────────────── Time Step 1 ────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭──────────────────── xor_hid ────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │ │ ┃ ║\n ║ ┃ │ ╰─────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └─────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 5.0 │ │ ┃ ║\n ║ ┃ │ │ output: 0.993 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933037241686309]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 1 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[0, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 1.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.0 1.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0.99999953 0.99999953 0.99999953 0.99999953 │ │ ┃ ║\n ║ ┃ │ │ 0.99999953 0.99999953 │ │ ┃ ║\n ║ ┃ │ │ 0.99999953 0.99999953 0.99999953 0.99999953]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 │ │ ┃ ║\n ║ ┃ │ │ 0.731 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 7.31 │ │ ┃ ║\n ║ ┃ │ │ output: 0.999 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999331517619013]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 2 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 0.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0.99999953 0.99999953 0.99999953 0.99999953 │ │ ┃ ║\n ║ ┃ │ │ 0.99999953 0.99999953 │ │ ┃ ║\n ║ ┃ │ │ 0.99999953 0.99999953 0.99999953 0.99999953]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 │ │ ┃ ║\n ║ ┃ │ │ 0.731 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 7.31 │ │ ┃ ║\n ║ ┃ │ │ output: 0.999 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999331517619013]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 3 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1, 1], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1. 1.]] │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 1.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1.99999906 1.99999906 1.99999906 1.99999906 │ │ ┃ ║\n ║ ┃ │ │ 1.99999906 1.99999906 │ │ ┃ ║\n ║ ┃ │ │ 1.99999906 1.99999906 1.99999906 1.99999906]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.881 0.881 0.881 0.881 0.881 0.881 0.881 0.881 0.881 │ │ ┃ ║\n ║ ┃ │ │ 0.881 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌─ Time Step 2 ──┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭── xor_out ──╮ │ ┃ ║\n ║ ┃ │ │ input: 8.81 │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 │ │ ┃ ║\n ║ ┃ │ ╰─────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └─────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998503501251857]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\n' + expected_report = '\n ╔════════════════════ EXECUTION OF autodiff_composition ═════════════════════╗\n ║ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━ autodiff_composition: Trial 0 ━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[0.0, 0.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.0 0.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌─────────────────── Time Step 1 ────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭──────────────────── xor_hid ────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │ │ ┃ ║\n ║ ┃ │ ╰─────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └─────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 5.0 │ │ ┃ ║\n ║ ┃ │ │ output: 0.993 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9933037241686309]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 1 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[0.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0. 1.]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.0 1.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0.99999953 0.99999953 0.99999953 0.99999953 │ │ ┃ ║\n ║ ┃ │ │ 0.99999953 0.99999953 │ │ ┃ ║\n ║ ┃ │ │ 0.99999953 0.99999953 0.99999953 0.99999953]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 │ │ ┃ ║\n ║ ┃ │ │ 0.731 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 7.31 │ │ ┃ ║\n ║ ┃ │ │ output: 0.999 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999331517619013]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 2 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 0.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1. 0.]] │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 0.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[0.99999953 0.99999953 0.99999953 0.99999953 │ │ ┃ ║\n ║ ┃ │ │ 0.99999953 0.99999953 │ │ ┃ ║\n ║ ┃ │ │ 0.99999953 0.99999953 0.99999953 0.99999953]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 0.731 │ │ ┃ ║\n ║ ┃ │ │ 0.731 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌── Time Step 2 ───┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─── xor_out ───╮ │ ┃ ║\n ║ ┃ │ │ input: 7.31 │ │ ┃ ║\n ║ ┃ │ │ output: 0.999 │ │ ┃ ║\n ║ ┃ │ ╰───────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.999331517619013]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ║ ┏━━━━━━━━━━━━━━━━━━━ autodiff_composition: Trial 3 ━━━━━━━━━━━━━━━━━━━━┓ ║\n ║ ┃ ┃ ║\n ║ ┃ input: [[1.0, 1.0], [0.5]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌──── Time Step 0 ────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭───── xor_in ─────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1. 1.]] │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 1.0 │ │ ┃ ║\n ║ ┃ │ ╰──────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └──────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌────────────────────────── Time Step 1 ───────────────────────────┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭─────────────────────────── xor_hid ───────────────────────────╮ │ ┃ ║\n ║ ┃ │ │ input: [[1.99999906 1.99999906 1.99999906 1.99999906 │ │ ┃ ║\n ║ ┃ │ │ 1.99999906 1.99999906 │ │ ┃ ║\n ║ ┃ │ │ 1.99999906 1.99999906 1.99999906 1.99999906]] │ │ ┃ ║\n ║ ┃ │ │ output: 0.881 0.881 0.881 0.881 0.881 0.881 0.881 0.881 0.881 │ │ ┃ ║\n ║ ┃ │ │ 0.881 │ │ ┃ ║\n ║ ┃ │ ╰───────────────────────────────────────────────────────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └───────────────────────────────────────────────────────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ ┌─ Time Step 2 ──┐ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ │ ╭── xor_out ──╮ │ ┃ ║\n ║ ┃ │ │ input: 8.81 │ │ ┃ ║\n ║ ┃ │ │ output: 1.0 │ │ ┃ ║\n ║ ┃ │ ╰─────────────╯ │ ┃ ║\n ║ ┃ │ │ ┃ ║\n ║ ┃ └─────────────────┘ ┃ ║\n ║ ┃ ┃ ║\n ║ ┃ result: [[0.9998503501251857]] ┃ ║\n ║ ┃ ┃ ║\n ║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║\n ║ ║\n ╚═════════════════════════════════════════════════════════════════════════════╝\n\n' assert actual_report == expected_report diff --git a/tests/functions/test_memory.py b/tests/functions/test_memory.py index c3f61f6e590..fe712bc49bb 100644 --- a/tests/functions/test_memory.py +++ b/tests/functions/test_memory.py @@ -219,7 +219,7 @@ def test_DictionaryMemory_with_initializer_and_key_size_same_as_val_size(self): em.function.duplicate_keys = False stim = 'A' - text = r'More than one item matched key \(\[1 2 3\]\) in memory for DictionaryMemory' + text = r'More than one item matched key \(\[1. 2. 3.\]\) in memory for DictionaryMemory' with pytest.warns(UserWarning, match=text): retrieved = em.execute(stimuli[stim]) @@ -271,7 +271,7 @@ def test_DictionaryMemory_with_initializer_and_key_size_diff_from_val_size(self) em.function.duplicate_keys = False stim = 'A' - text = r'More than one item matched key \(\[1 2 3\]\) in memory for DictionaryMemory' + text = r'More than one item matched key \(\[1. 2. 3.\]\) in memory for DictionaryMemory' with pytest.warns(UserWarning, match=text): retrieved = em.execute(stimuli[stim]) @@ -341,7 +341,7 @@ def test_DictionaryMemory_without_initializer_and_key_size_same_as_val_size(self em.function.duplicate_keys = False stim = 'A' - text = r'More than one item matched key \(\[1 2 3\]\) in memory for DictionaryMemory' + text = r'More than one item matched key \(\[1. 2. 3.\]\) in memory for DictionaryMemory' with pytest.warns(UserWarning, match=text): retrieved = em.execute(stimuli[stim]) @@ -391,7 +391,7 @@ def test_DictionaryMemory_without_initializer_and_key_size_diff_from_val_size(se em.function.duplicate_keys = False stim = 'A' - text = r'More than one item matched key \(\[1 2 3\]\) in memory for DictionaryMemory' + text = r'More than one item matched key \(\[1. 2. 3.\]\) in memory for DictionaryMemory' with pytest.warns(UserWarning, match=text): retrieved = em.execute(stimuli[stim]) diff --git a/tests/mechanisms/test_ddm_mechanism.py b/tests/mechanisms/test_ddm_mechanism.py index ef9f2ac0643..75fb1f82164 100644 --- a/tests/mechanisms/test_ddm_mechanism.py +++ b/tests/mechanisms/test_ddm_mechanism.py @@ -9,6 +9,7 @@ from psyneulink.core.components.functions.nonstateful.distributionfunctions import DriftDiffusionAnalytical, NormalDist from psyneulink.core.components.functions.function import FunctionError from psyneulink.core.components.functions.stateful.integratorfunctions import DriftDiffusionIntegrator +from psyneulink.core.components.mechanisms.mechanism import MechanismError from psyneulink.core.components.mechanisms.processing.processingmechanism import ProcessingMechanism from psyneulink.core.compositions.composition import Composition from psyneulink.core.scheduling.condition import Never, WhenFinished @@ -225,13 +226,16 @@ def test_selected_input_array(self): output_ports=[SELECTED_INPUT_ARRAY], name='DDM' ) - action_selection.execute([1.0]) + with pytest.raises(MechanismError) as error: + action_selection.execute([1.0]) + assert 'Length (1) of input ([1.]) does not match required length (2) ' \ + 'for input to InputPort \'ARRAY\' of DDM.' in str(error.value) + action_selection.execute([1.0, 0.0]) # ------------------------------------------------------------------------------------------------ # TEST 2 # function = Bogacz - @pytest.mark.ddm_mechanism @pytest.mark.mechanism @pytest.mark.benchmark @@ -382,20 +386,20 @@ def test_DDM_input_list_len_2(): def test_DDM_input_fn(): - with pytest.raises(TypeError) as error_text: + with pytest.raises(MechanismError) as error_text: stim = NormalDist() T = DDM( name='DDM', function=DriftDiffusionIntegrator( - noise=0.0, rate=1.0, time_step_size=1.0 ), ) float(T.execute(stim)) - assert "not supported for the input types" in str(error_text.value) - + assert '"Input to \'DDM\' ([(NormalDist Normal Distribution Function' in str(error_text.value) + assert 'is incompatible with its corresponding InputPort (DDM[InputPort-0]): ' \ + '\'unsupported operand type(s) for *: \'NormalDist\' and \'float\'.\'"' in str(error_text.value) # ======================================= RATE TESTS ============================================ diff --git a/tests/mechanisms/test_kwta.py b/tests/mechanisms/test_kwta.py index 15115567376..905e5d33df5 100644 --- a/tests/mechanisms/test_kwta.py +++ b/tests/mechanisms/test_kwta.py @@ -51,13 +51,14 @@ def test_kwta_no_inputs(self): assert(np.allclose(val, [[0.5]])) def test_kwta_inputs_list_of_strings(self): - with pytest.raises(KWTAError) as error_text: + with pytest.raises(MechanismError) as error_text: K = KWTAMechanism( name='K', size = 4, ) K.execute(["one", "two", "three", "four"]) - assert("which is not supported for KWTA" in str(error_text.value)) + assert('"Input to \'K\' ([\'one\' \'two\' \'three\' \'four\']) is incompatible with its corresponding ' + 'InputPort (K[InputPort-0]): \'cannot perform reduce with flexible type.\'"' in str(error_text.value)) def test_kwta_var_list_of_strings(self): with pytest.raises(ParameterError) as error_text: diff --git a/tests/mechanisms/test_recurrent_transfer_mechanism.py b/tests/mechanisms/test_recurrent_transfer_mechanism.py index 2a24df650c0..6fc87408ffb 100644 --- a/tests/mechanisms/test_recurrent_transfer_mechanism.py +++ b/tests/mechanisms/test_recurrent_transfer_mechanism.py @@ -206,14 +206,16 @@ def test_recurrent_mech_no_inputs(self, benchmark, mech_mode): benchmark(EX, [1]) def test_recurrent_mech_inputs_list_of_strings(self): - with pytest.raises(FunctionError) as error_text: + with pytest.raises(MechanismError) as error_text: R = RecurrentTransferMechanism( name='R', default_variable=[0, 0, 0, 0], integrator_mode=True ) R.execute(["one", "two", "three", "four"]) - assert "Unrecognized type" in str(error_text.value) + assert '"Input to \'R\' ([\'one\' \'two\' \'three\' \'four\']) is incompatible ' \ + 'with its corresponding InputPort (R[InputPort-0]): ' \ + '\'cannot perform reduce with flexible type.\'"' in str(error_text.value) def test_recurrent_mech_var_list_of_strings(self): with pytest.raises(ParameterError) as error_text: diff --git a/tests/mechanisms/test_transfer_mechanism.py b/tests/mechanisms/test_transfer_mechanism.py index ac528e7fdd0..34ffa016e2e 100644 --- a/tests/mechanisms/test_transfer_mechanism.py +++ b/tests/mechanisms/test_transfer_mechanism.py @@ -101,14 +101,16 @@ def test_transfer_mech_variable_none_size_none(self): @pytest.mark.mechanism @pytest.mark.transfer_mechanism def test_transfer_mech_inputs_list_of_strings(self): - with pytest.raises(FunctionError) as error_text: + with pytest.raises(MechanismError) as error_text: T = TransferMechanism( name='T', default_variable=[0, 0, 0, 0], integrator_mode=True ) T.execute(["one", "two", "three", "four"]) - assert "Unrecognized type" in str(error_text.value) + assert '"Input to \'T\' ([\'one\' \'two\' \'three\' \'four\']) is incompatible ' \ + 'with its corresponding InputPort (T[InputPort-0]): ' \ + '\'cannot perform reduce with flexible type.\'"' in str(error_text.value) @pytest.mark.mechanism @pytest.mark.transfer_mechanism From 283ae69ffe28d7221097f587cb22ab6313a0ba6e Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 4 Feb 2022 11:21:04 -0500 Subject: [PATCH 126/285] llvm/{mechanism,port}: Do not create local copy of parameters if there's no modulation The compiler is not able to eliminate these. Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 15 +++++++++------ psyneulink/core/components/ports/port.py | 12 +++++++++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 2309bdab87d..1da719fe116 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2958,17 +2958,20 @@ def _fill_input(b, p_input, i): def _gen_llvm_param_ports_for_obj(self, obj, params_in, ctx, builder, mech_params, mech_state, mech_input): - # Allocate a shadow structure to overload user supplied parameters - params_out = builder.alloca(params_in.type.pointee) - # Copy original values. This handles params without param ports. - # Few extra copies will be eliminated by the compiler. - builder.store(builder.load(params_in), params_out) - # This should be faster than 'obj._get_compilation_params' compilation_params = (getattr(obj.parameters, p_id, None) for p_id in obj.llvm_param_ids) # Filter out param ports without corresponding param for this function param_ports = [self._parameter_ports[param] for param in compilation_params if param in self._parameter_ports] + # Exit early if there's no modulation. LLVM is not aliminating + # the redundant copy created below. + if len(param_ports) == 0: + return params_in, builder + + # Allocate a shadow structure to overload user supplied parameters + params_out = builder.alloca(params_in.type.pointee) + builder.store(builder.load(params_in), params_out) + def _get_output_ptr(b, i): ptr = pnlvm.helpers.get_param_ptr(b, obj, params_out, param_ports[i].source.name) diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index 9784d0db5fc..f420561f698 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -2286,11 +2286,17 @@ def _get_input_struct_type(self, ctx): def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, *, tags:frozenset): port_f = ctx.import_llvm_function(self.function) - # Create a local copy of the function parameters base_params = pnlvm.helpers.get_param_ptr(builder, self, params, "function") - f_params = builder.alloca(port_f.args[0].type.pointee) - builder.store(builder.load(base_params), f_params) + + if len(self.mod_afferents) > 0: + # Create a local copy of the function parameters + # only if there are modulating projections + # LLVM is not eliminating the redundant copy + f_params = builder.alloca(port_f.args[0].type.pointee) + builder.store(builder.load(base_params), f_params) + else: + f_params = base_params # FIXME: Handle and combine multiple afferents assert len(self.mod_afferents) <= 1 From cbb51bbe7c59ea10d48929493c726ecf4f7fb2aa Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 4 Feb 2022 18:42:33 -0500 Subject: [PATCH 127/285] llvm: Add human readable names to most alloca ops for mechanism and composition functions Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 13 +++++----- .../control/optimizationcontrolmechanism.py | 12 ++++++---- .../processing/transfermechanism.py | 4 ++-- .../ports/modulatorysignals/controlsignal.py | 8 ++++--- psyneulink/core/components/ports/port.py | 3 ++- psyneulink/core/llvm/builtins.py | 24 +++++++++---------- psyneulink/core/llvm/codegen.py | 16 +++++++------ psyneulink/core/llvm/helpers.py | 2 +- .../control/agt/lccontrolmechanism.py | 2 +- .../mechanisms/processing/integrator/ddm.py | 4 ++-- .../transfer/recurrenttransfermechanism.py | 8 +++---- 11 files changed, 52 insertions(+), 44 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 1da719fe116..b53399dc5f7 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2896,7 +2896,8 @@ def _gen_llvm_ports(self, ctx, builder, ports, group, builder, p_output = get_output_ptr(builder, i) # Allocate the input structure (data + modulation) - p_input = builder.alloca(p_function.args[2].type.pointee) + p_input = builder.alloca(p_function.args[2].type.pointee, + name=group + "_port_" + str(i) + "_input") # Copy input data to input structure builder = fill_input_data(builder, p_input, i) @@ -2936,7 +2937,7 @@ def _gen_llvm_input_ports(self, ctx, builder, else: ip_output_type = pnlvm.ir.LiteralStructType(ip_output_list) - ip_output = builder.alloca(ip_output_type) + ip_output = builder.alloca(ip_output_type, name="input_ports_out") def _get_output_ptr(b, i): ptr = b.gep(ip_output, [ctx.int32_ty(0), ctx.int32_ty(i)]) @@ -2969,7 +2970,7 @@ def _gen_llvm_param_ports_for_obj(self, obj, params_in, ctx, builder, return params_in, builder # Allocate a shadow structure to overload user supplied parameters - params_out = builder.alloca(params_in.type.pointee) + params_out = builder.alloca(params_in.type.pointee, name="modulated_parameters") builder.store(builder.load(params_in), params_out) def _get_output_ptr(b, i): @@ -3040,7 +3041,7 @@ def _fill_input(b, s_input, i): def _gen_llvm_invoke_function(self, ctx, builder, function, params, state, variable, *, tags:frozenset): fun = ctx.import_llvm_function(function, tags=tags) - fun_out = builder.alloca(fun.args[3].type.pointee) + fun_out = builder.alloca(fun.args[3].type.pointee, name=function.name + "_output") builder.call(fun, [params, state, variable, fun_out]) @@ -3107,8 +3108,8 @@ def _gen_llvm_function_reset(self, ctx, builder, params, state, arg_in, arg_out, reinit_func = ctx.import_llvm_function(self.function, tags=tags) reinit_params = pnlvm.helpers.get_param_ptr(builder, self, params, "function") reinit_state = pnlvm.helpers.get_state_ptr(builder, self, state, "function") - reinit_in = builder.alloca(reinit_func.args[2].type.pointee) - reinit_out = builder.alloca(reinit_func.args[3].type.pointee) + reinit_in = builder.alloca(reinit_func.args[2].type.pointee, name="reinit_in") + reinit_out = builder.alloca(reinit_func.args[3].type.pointee, name="reinit_out") builder.call(reinit_func, [reinit_params, reinit_state, reinit_in, reinit_out]) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 73f52169b3f..efc080c51c3 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -2800,7 +2800,7 @@ def _gen_llvm_net_outcome_function(self, *, ctx, tags=frozenset()): "output_ports", None) # calculate cost function - total_cost = builder.alloca(ctx.float_ty) + total_cost = builder.alloca(ctx.float_ty, name="total_cost") builder.store(ctx.float_ty(-0.0), total_cost) for i, op in enumerate(self.output_ports): op_i_params = builder.gep(op_params, [ctx.int32_ty(0), @@ -2810,7 +2810,8 @@ def _gen_llvm_net_outcome_function(self, *, ctx, tags=frozenset()): op_f = ctx.import_llvm_function(op, tags=frozenset({"costs"})) - op_in = builder.alloca(op_f.args[2].type.pointee) + op_in = builder.alloca(op_f.args[2].type.pointee, + name="output_port_cost_in") # copy allocation_sample, the input is 1-element array in a struct data_in = builder.gep(allocation_sample, [ctx.int32_ty(0), @@ -2873,7 +2874,7 @@ def _gen_llvm_evaluate_alloc_range_function(self, *, ctx:pnlvm.LLVMBuilderContex search_space = pnlvm.helpers.get_param_ptr(builder, self.function, func_params, "search_space") - allocation = builder.alloca(evaluate_f.args[2].type.pointee) + allocation = builder.alloca(evaluate_f.args[2].type.pointee, name="allocation") with pnlvm.helpers.for_loop(builder, start, stop, stop.type(1), "alloc_loop") as (b, idx): func_out = b.gep(arg_out, [idx]) @@ -3065,7 +3066,7 @@ def _gen_llvm_function(self, *, ctx:pnlvm.LLVMBuilderContext, tags:frozenset): def _gen_llvm_invoke_function(self, ctx, builder, function, params, context, variable, *, tags:frozenset): fun = ctx.import_llvm_function(function) - fun_out = builder.alloca(fun.args[3].type.pointee) + fun_out = builder.alloca(fun.args[3].type.pointee, name="func_out") args = [params, context, variable, fun_out] # If we're calling compiled version of Composition.evaluate, @@ -3079,7 +3080,8 @@ def _gen_llvm_invoke_function(self, ctx, builder, function, params, context, var def _gen_llvm_output_port_parse_variable(self, ctx, builder, params, context, value, port): i = self.output_ports.index(port) # Allocate the only member of the port input struct - oport_input = builder.alloca(ctx.get_input_struct_type(port).elements[0]) + oport_input = builder.alloca(ctx.get_input_struct_type(port).elements[0], + name="output_port_in") # FIXME: workaround controller signals occasionally being 2d dest_ptr = pnlvm.helpers.unwrap_2d_array(builder, oport_input) dest_ptr = builder.gep(dest_ptr, [ctx.int32_ty(0), ctx.int32_ty(0)]) diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index 7cb05c78adc..10da9fba668 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -1549,7 +1549,7 @@ def _gen_llvm_is_finished_cond(self, ctx, builder, params, state): ctx.int32_ty(0)]) threshold = builder.load(threshold_ptr) - cmp_val_ptr = builder.alloca(threshold.type) + cmp_val_ptr = builder.alloca(threshold.type, name="is_finished_value") if self.termination_measure is max: assert self._termination_measure_num_items_expected == 1 # Get inside of the structure @@ -1578,7 +1578,7 @@ def _gen_llvm_is_finished_cond(self, ctx, builder, params, state): func = ctx.import_llvm_function(self.termination_measure) func_params = pnlvm.helpers.get_param_ptr(builder, self, params, "termination_measure") func_state = pnlvm.helpers.get_state_ptr(builder, self, state, "termination_measure") - func_in = builder.alloca(func.args[2].type.pointee) + func_in = builder.alloca(func.args[2].type.pointee, name="is_finished_func_in") # Populate input func_in_current_ptr = builder.gep(func_in, [ctx.int32_ty(0), ctx.int32_ty(0)]) diff --git a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py index f9e01347b1e..d433b4bb036 100644 --- a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py @@ -1152,9 +1152,10 @@ def _gen_llvm_costs(self, *, ctx:pnlvm.LLVMBuilderContext, tags:frozenset): assert self.cost_options & ~CostFunctions.INTENSITY == 0 cfunc = ctx.import_llvm_function(self.function.combine_costs_fct) - cfunc_in = builder.alloca(cfunc.args[2].type.pointee) + cfunc_in = builder.alloca(cfunc.args[2].type.pointee, + name="combine_costs_func_in") - # Set to 0 be default + # Set to 0 by default builder.store(cfunc_in.type.pointee(None), cfunc_in) cost_funcs = 0 @@ -1189,7 +1190,8 @@ def _gen_llvm_costs(self, *, ctx:pnlvm.LLVMBuilderContext, tags:frozenset): cfunc_state = pnlvm.helpers.get_state_ptr(builder, self.function, func_state, "combine_costs_fct") - cfunc_out = builder.alloca(cfunc.args[3].type.pointee) + cfunc_out = builder.alloca(cfunc.args[3].type.pointee, + name="combine_costs_func_out") builder.call(cfunc, [cfunc_params, cfunc_state, cfunc_in, cfunc_out]) diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index f420561f698..baf9e9e2353 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -2293,7 +2293,8 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, # Create a local copy of the function parameters # only if there are modulating projections # LLVM is not eliminating the redundant copy - f_params = builder.alloca(port_f.args[0].type.pointee) + f_params = builder.alloca(port_f.args[0].type.pointee, + name="modulated_port_params") builder.store(builder.load(base_params), f_params) else: f_params = base_params diff --git a/psyneulink/core/llvm/builtins.py b/psyneulink/core/llvm/builtins.py index 859a740af0c..57bb6bb88de 100644 --- a/psyneulink/core/llvm/builtins.py +++ b/psyneulink/core/llvm/builtins.py @@ -543,13 +543,13 @@ def _setup_mt_rand_init(ctx, state_ty, init_scalar): builder.call(init_scalar, [state, default_seed]) # python considers everything to be an array - key_array = builder.alloca(ir.ArrayType(seed.type, 1)) + key_array = builder.alloca(ir.ArrayType(seed.type, 1), name="key_array") key_p = builder.gep(key_array, [ctx.int32_ty(0), ctx.int32_ty(0)]) builder.store(seed, key_p) - pi = builder.alloca(ctx.int32_ty) + pi = builder.alloca(ctx.int32_ty, name="pi_slot") builder.store(ctx.int32_ty(1), pi) - pj = builder.alloca(ctx.int32_ty) + pj = builder.alloca(ctx.int32_ty, name="pj_slot") builder.store(ctx.int32_ty(0), pj) array = builder.gep(state, [ctx.int32_ty(0), ctx.int32_ty(0)]) a_0 = builder.gep(array, [ctx.int32_ty(0), ctx.int32_ty(0)]) @@ -640,7 +640,7 @@ def _setup_mt_rand_integer(ctx, state_ty): cond = builder.icmp_signed(">=", idx, ctx.int32_ty(_MERSENNE_N)) with builder.if_then(cond, likely=False): mag01 = ir.ArrayType(array.type.pointee.element, 2)([0, 0x9908b0df]) - pmag01 = builder.alloca(mag01.type) + pmag01 = builder.alloca(mag01.type, name="mag01") builder.store(mag01, pmag01) with helpers.for_loop_zero_inc(builder, @@ -741,10 +741,10 @@ def _setup_mt_rand_float(ctx, state_ty, gen_int): builder = _setup_builtin_func_builder(ctx, "mt_rand_double", (state_ty.as_pointer(), ctx.float_ty.as_pointer())) state, out = builder.function.args - al = builder.alloca(gen_int.args[1].type.pointee) + al = builder.alloca(gen_int.args[1].type.pointee, name="al_gen_int") builder.call(gen_int, [state, al]) - bl = builder.alloca(gen_int.args[1].type.pointee) + bl = builder.alloca(gen_int.args[1].type.pointee, name="bl_gen_int") builder.call(gen_int, [state, bl]) a = builder.load(al) @@ -802,7 +802,7 @@ def _setup_mt_rand_normal(ctx, state_ty, gen_float): builder.branch(loop_block) builder.position_at_end(loop_block) - tmp = builder.alloca(out.type.pointee) + tmp = builder.alloca(out.type.pointee, name="mt_rand_normal_tmp") # X1 is in (-1, 1) builder.call(gen_float, [state, tmp]) @@ -1085,7 +1085,7 @@ def _setup_philox_rand_int32(ctx, state_ty, gen_int64): builder.ret_void() - val_ptr = builder.alloca(gen_int64.args[1].type.pointee) + val_ptr = builder.alloca(gen_int64.args[1].type.pointee, name="rand_i64") builder.call(gen_int64, [state, val_ptr]) val = builder.load(val_ptr) @@ -1112,7 +1112,7 @@ def _setup_philox_rand_double(ctx, state_ty, gen_int64): rhs = double_ty(1.0 / 9007199254740992.0) # Generate random integer - lhs_ptr = builder.alloca(gen_int64.args[1].type.pointee) + lhs_ptr = builder.alloca(gen_int64.args[1].type.pointee, name="rand_int64") builder.call(gen_int64, [state, lhs_ptr]) # convert to float @@ -1138,7 +1138,7 @@ def _setup_philox_rand_float(ctx, state_ty, gen_int32): rhs = float_ty(1.0 / 8388608.0) # Generate random integer - lhs_ptr = builder.alloca(gen_int32.args[1].type.pointee) + lhs_ptr = builder.alloca(gen_int32.args[1].type.pointee, name="rand_int32") builder.call(gen_int32, [state, lhs_ptr]) # convert to float @@ -1880,8 +1880,8 @@ def _setup_philox_rand_normal(ctx, state_ty, gen_float, gen_int, wi_data, ki_dat # Allocate storage for calling int/float PRNG # outside of the loop - tmp_fptype = builder.alloca(fptype) - tmp_itype = builder.alloca(itype) + tmp_fptype = builder.alloca(fptype, name="tmp_fp") + tmp_itype = builder.alloca(itype, name="tmp_int") # Enter the main generation loop builder.branch(loop_block) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index fb780a6a50f..8c5adcec20b 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -573,13 +573,15 @@ def gen_node_wrapper(ctx, composition, node, *, tags:frozenset): # And we run no further projection incoming_projections = [] elif not is_mech: - node_in = builder.alloca(node_function.args[2].type.pointee) + node_in = builder.alloca(node_function.args[2].type.pointee, + name="composition_node_input") incoming_projections = node.input_CIM.afferents + node.parameter_CIM.afferents else: # this path also handles parameter_CIM with no afferent # projections. 'comp_in' does not include any extra values, # and the entire call should be optimized out. - node_in = builder.alloca(node_function.args[2].type.pointee) + node_in = builder.alloca(node_function.args[2].type.pointee, + name="mechanism_node_input") incoming_projections = node.afferents if "reset" in tags or "is_finished" in tags: @@ -716,7 +718,7 @@ def _gen_composition_exec_context(ctx, composition, *, tags:frozenset, suffix="" builder.store(const_params, params) if "alloca_data" in debug_env: - data = builder.alloca(data_arg.type.pointee) + data = builder.alloca(data_arg.type.pointee, name="data_loc") data_vals = builder.load(data_arg) builder.store(data_vals, data) else: @@ -755,7 +757,7 @@ def gen_composition_exec(ctx, composition, *, tags:frozenset): nodes_states = helpers.get_state_ptr(builder, composition, state, "nodes") # Allocate temporary output storage - output_storage = builder.alloca(data.type.pointee, name="output_storage") + output_storage = builder.alloca(data.type.pointee, name="comp_output_frozen_temp") # Get locations of number of executions. num_exec_locs = {} @@ -963,7 +965,7 @@ def gen_composition_run(ctx, composition, *, tags:frozenset): if not simulation and "const_data" in debug_env: const_data = data.type.pointee(composition._get_data_initializer(None)) - data = builder.alloca(data.type.pointee) + data = builder.alloca(data.type.pointee, name="const_data_loc") builder.store(const_data, data) # Hardcode stateful parameters if set in the environment @@ -986,12 +988,12 @@ def gen_composition_run(ctx, composition, *, tags:frozenset): # Allocate and initialize condition structure cond_gen = helpers.ConditionGenerator(ctx, composition) cond_type = cond_gen.get_condition_struct_type() - cond = builder.alloca(cond_type) + cond = builder.alloca(cond_type, name="scheduler_metadata") cond_init = cond_type(cond_gen.get_condition_initializer()) builder.store(cond_init, cond) trials = builder.load(trials_ptr, "trials") - iters_ptr = builder.alloca(trials.type) + iters_ptr = builder.alloca(trials.type, name="iterations") builder.store(iters_ptr.type.pointee(0), iters_ptr) # Start the main loop structure diff --git a/psyneulink/core/llvm/helpers.py b/psyneulink/core/llvm/helpers.py index 22e45c1e236..ec951a9dc0c 100644 --- a/psyneulink/core/llvm/helpers.py +++ b/psyneulink/core/llvm/helpers.py @@ -192,7 +192,7 @@ def is_close(ctx, builder, val1, val2, rtol=1e-05, atol=1e-08): def all_close(ctx, builder, arr1, arr2, rtol=1e-05, atol=1e-08): assert arr1.type == arr2.type - all_ptr = builder.alloca(ir.IntType(1)) + all_ptr = builder.alloca(ir.IntType(1), name="all_close_slot") builder.store(all_ptr.type.pointee(1), all_ptr) with array_ptr_loop(builder, arr1, "all_close") as (b1, idx): val1_ptr = b1.gep(arr1, [idx.type(0), idx]) diff --git a/psyneulink/library/components/mechanisms/modulatory/control/agt/lccontrolmechanism.py b/psyneulink/library/components/mechanisms/modulatory/control/agt/lccontrolmechanism.py index e68c13b57d1..bcee443a12e 100644 --- a/psyneulink/library/components/mechanisms/modulatory/control/agt/lccontrolmechanism.py +++ b/psyneulink/library/components/mechanisms/modulatory/control/agt/lccontrolmechanism.py @@ -844,7 +844,7 @@ def _gen_llvm_invoke_function(self, ctx, builder, function, params, state, varia elements_ty = pnlvm.ir.LiteralStructType(elements) # allocate new output type - new_out = builder.alloca(elements_ty) + new_out = builder.alloca(elements_ty, name="function_out") # Load mechanism parameters params = builder.function.args[0] diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index ca37018a3a7..51902ac576c 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -1082,7 +1082,7 @@ def _gen_llvm_invoke_function(self, ctx, builder, function, params, state, varia mf_out, builder = super()._gen_llvm_invoke_function(ctx, builder, function, params, state, variable, tags=tags) mech_out_ty = ctx.convert_python_struct_to_llvm_ir(self.defaults.value) - mech_out = builder.alloca(mech_out_ty) + mech_out = builder.alloca(mech_out_ty, name="mech_out") if isinstance(self.function, IntegratorFunction): # Integrator version of the DDM mechanism converts the @@ -1129,7 +1129,7 @@ def _gen_llvm_invoke_function(self, ctx, builder, function, params, state, varia mech_state = builder.function.args[1] random_state = ctx.get_random_state_ptr(builder, self, mech_state, mech_params) random_f = ctx.get_uniform_dist_function_by_state(random_state) - random_val_ptr = builder.alloca(random_f.args[1].type.pointee) + random_val_ptr = builder.alloca(random_f.args[1].type.pointee, name="random_out") builder.call(random_f, [random_state, random_val_ptr]) random_val = builder.load(random_val_ptr) diff --git a/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py index 56cdc5ab265..bd300b0c98c 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py @@ -1292,8 +1292,8 @@ def _gen_llvm_function_reset(self, ctx, builder, params, state, arg_in, arg_out, reinit_func = ctx.import_llvm_function(self.function, tags=tags) reinit_params = pnlvm.helpers.get_param_ptr(builder, self, params, "function") reinit_state = pnlvm.helpers.get_state_ptr(builder, self, state, "function") - reinit_in = builder.alloca(reinit_func.args[2].type.pointee) - reinit_out = builder.alloca(reinit_func.args[3].type.pointee) + reinit_in = builder.alloca(reinit_func.args[2].type.pointee, name="reinit_in") + reinit_out = builder.alloca(reinit_func.args[3].type.pointee, name="reinit_out") builder.call(reinit_func, [reinit_params, reinit_state, reinit_in, reinit_out]) @@ -1301,8 +1301,8 @@ def _gen_llvm_function_reset(self, ctx, builder, params, state, arg_in, arg_out, if self.integrator_mode: reinit_f = ctx.import_llvm_function(self.integrator_function, tags=tags) - reinit_in = builder.alloca(reinit_f.args[2].type.pointee) - reinit_out = builder.alloca(reinit_f.args[3].type.pointee) + reinit_in = builder.alloca(reinit_f.args[2].type.pointee, name="integ_reinit_in") + reinit_out = builder.alloca(reinit_f.args[3].type.pointee, name="integ_reinit_out") reinit_params = pnlvm.helpers.get_param_ptr(builder, self, params, "integrator_function") reinit_state = pnlvm.helpers.get_state_ptr(builder, self, state, "integrator_function") builder.call(reinit_f, [reinit_params, reinit_state, reinit_in, From a278089c4545f85f45b3932bdc8c6685efa29daf Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 4 Feb 2022 14:35:50 -0500 Subject: [PATCH 128/285] llvm/cuda: Drop 'evaluate' specific codepaths in kernel wrapper This has not been used since GPU accelerated evaluations were switched to range_evaluate. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/builder_context.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index 23ab4da929d..b22ba1c2c93 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -503,11 +503,6 @@ def _gen_cuda_kernel_wrapper_module(function): builder.ret_void() return module - - # There are 6 arguments to evaluate: - # comp_param, comp_state, allocations, output, input, comp_data - is_grid_evaluate = len(args) == 6 - # Runs need special handling. data_in and data_out are one dimensional, # but hold entries for all parallel invocations. # comp_state, comp_params, comp_data, comp_in, comp_out, #trials, #inputs @@ -528,10 +523,6 @@ def _gen_cuda_kernel_wrapper_module(function): offset = builder.mul(global_id, runs_count) elif i == 3: # data_in offset = builder.mul(global_id, input_count) - elif is_grid_evaluate: - # all but #2 and #3 are shared - if i != 2 and i != 3: - offset = ir.IntType(32)(0) arg = builder.gep(arg, [offset]) From bee9e59d26bdb55616b87c1aa4f5bd871781e2e6 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 4 Feb 2022 21:05:48 -0500 Subject: [PATCH 129/285] llvm: Add a memcopy helper and use it to copy large structures A 4B aligned version performs better on GPUs. A llvm.memcopy fallback is available via PNL_LLVM_DEBUG=unaligned_copy. Both variants much more register efficient (and usually faster) than store(load(...)). Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 2 +- .../control/optimizationcontrolmechanism.py | 4 +-- psyneulink/core/llvm/debug.py | 1 + psyneulink/core/llvm/helpers.py | 31 +++++++++++++++++++ tests/llvm/test_debug_composition.py | 2 +- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index b53399dc5f7..f741f685de1 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2971,7 +2971,7 @@ def _gen_llvm_param_ports_for_obj(self, obj, params_in, ctx, builder, # Allocate a shadow structure to overload user supplied parameters params_out = builder.alloca(params_in.type.pointee, name="modulated_parameters") - builder.store(builder.load(params_in), params_out) + builder = pnlvm.helpers.memcpy(builder, params_out, params_in) def _get_output_ptr(b, i): ptr = pnlvm.helpers.get_param_ptr(b, obj, params_out, diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index efc080c51c3..929c998559a 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -2912,7 +2912,7 @@ def _gen_llvm_evaluate_function(self, *, ctx:pnlvm.LLVMBuilderContext, tags=froz const_state = self.agent_rep._get_state_initializer(None) builder.store(comp_state.type.pointee(const_state), comp_state) else: - builder.store(builder.load(base_comp_state), comp_state) + builder = pnlvm.helpers.memcpy(builder, comp_state, base_comp_state) # Create a simulation copy of composition data comp_data = builder.alloca(base_comp_data.type.pointee, name="data_copy") @@ -2920,7 +2920,7 @@ def _gen_llvm_evaluate_function(self, *, ctx:pnlvm.LLVMBuilderContext, tags=froz const_data = self.agent_rep._get_data_initializer(None) builder.store(comp_data.type.pointee(const_data), comp_data) else: - builder.store(builder.load(base_comp_data), comp_data) + builder = pnlvm.helpers.memcpy(builder, comp_data, base_comp_data) # Evaluate is called on composition controller assert self.composition.controller is self diff --git a/psyneulink/core/llvm/debug.py b/psyneulink/core/llvm/debug.py index 7db9a38c1c8..090ce8b775f 100644 --- a/psyneulink/core/llvm/debug.py +++ b/psyneulink/core/llvm/debug.py @@ -34,6 +34,7 @@ * "const_state" -- hardcode base context values into generate code, instead of laoding them from the context argument * "opt" -- Set compiler optimization level (0,1,2,3) + * "unaligned_copy" -- Do not assume structures are 4B aligned * "cuda_max_regs" -- Set maximum allowed GPU arch registers Compiled code dump: diff --git a/psyneulink/core/llvm/helpers.py b/psyneulink/core/llvm/helpers.py index ec951a9dc0c..13f2feaafcf 100644 --- a/psyneulink/core/llvm/helpers.py +++ b/psyneulink/core/llvm/helpers.py @@ -62,6 +62,37 @@ def array_ptr_loop(builder, array, id): stop = ir.IntType(32)(array.type.pointee.count) return for_loop_zero_inc(builder, stop, id) +def memcpy(builder, dst, src): + + bool_ty = ir.IntType(1) + char_ptr_ty = ir.IntType(8).as_pointer() + ptr_src = builder.bitcast(src, char_ptr_ty) + ptr_dst = builder.bitcast(dst, char_ptr_ty) + + obj_size_ty = ir.FunctionType(ir.IntType(64), [char_ptr_ty, bool_ty, bool_ty, bool_ty]) + obj_size_f = builder.function.module.declare_intrinsic("llvm.objectsize.i64", [], obj_size_ty) + # the params are: obj pointer, 0 on unknown size, NULL is unknown, size at runtime + obj_size = builder.call(obj_size_f, [ptr_dst, bool_ty(1), bool_ty(0), bool_ty(0)]) + + if "unaligned_copy" in debug_env: + memcpy_ty = ir.FunctionType(ir.VoidType(), [char_ptr_ty, char_ptr_ty, obj_size.type, bool_ty]) + memcpy_f = builder.function.module.declare_intrinsic("llvm.memcpy", [], memcpy_ty) + builder.call(memcpy_f, [ptr_dst, ptr_src, obj_size, bool_ty(0)]) + else: + int_ty = ir.IntType(32) + int_ptr_ty = int_ty.as_pointer() + obj_size = builder.add(obj_size, obj_size.type((int_ty.width // 8) - 1)) + obj_size = builder.udiv(obj_size, obj_size.type(int_ty.width // 8)) + ptr_src = builder.bitcast(src, int_ptr_ty) + ptr_dst = builder.bitcast(dst, int_ptr_ty) + + with for_loop_zero_inc(builder, obj_size, id="memcopy_loop") as (b, idx): + src = b.gep(ptr_src, [idx]) + dst = b.gep(ptr_dst, [idx]) + b.store(b.load(src), dst) + + return builder + def fclamp(builder, val, min_val, max_val): min_val = min_val if isinstance(min_val, ir.Value) else val.type(min_val) diff --git a/tests/llvm/test_debug_composition.py b/tests/llvm/test_debug_composition.py index b12698c9d13..5555e601791 100644 --- a/tests/llvm/test_debug_composition.py +++ b/tests/llvm/test_debug_composition.py @@ -9,7 +9,7 @@ from psyneulink.core.components.mechanisms.processing.transfermechanism import TransferMechanism from psyneulink.core.compositions.composition import Composition -debug_options=["const_input=[[[7]]]", "const_input", "const_data", "const_params", "const_data", "const_state", "stat", "time_stat"] +debug_options=["const_input=[[[7]]]", "const_input", "const_data", "const_params", "const_data", "const_state", "stat", "time_stat", "unaligned_copy"] options_combinations = (";".join(("debug_info", *c)) for i in range(len(debug_options) + 1) for c in combinations(debug_options, i)) @pytest.mark.composition From 16a1c42137674df732c20bf40c8d5c082936acbf Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 26 Jan 2022 01:36:39 -0500 Subject: [PATCH 130/285] llvm/cuda: Add option to upload arguments to shared memory Move all shared params for GPU grid evaluate. Use PNL_LLVM_DEBUG='cuda_no_shared' to opt out. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/builder_context.py | 66 ++++++++++++++++++++++--- psyneulink/core/llvm/jit_engine.py | 2 +- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index b22ba1c2c93..01f7a8d3a09 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -483,18 +483,71 @@ def _gen_cuda_kernel_wrapper_module(function): tid_x_f = ir.Function(module, intrin_ty, "llvm.nvvm.read.ptx.sreg.tid.x") ntid_x_f = ir.Function(module, intrin_ty, "llvm.nvvm.read.ptx.sreg.ntid.x") ctaid_x_f = ir.Function(module, intrin_ty, "llvm.nvvm.read.ptx.sreg.ctaid.x") - global_id = builder.mul(builder.call(ctaid_x_f, []), builder.call(ntid_x_f, [])) - global_id = builder.add(global_id, builder.call(tid_x_f, [])) + ntid = builder.call(ntid_x_f, []) + tid = builder.call(tid_x_f, []) + global_id = builder.mul(builder.call(ctaid_x_f, []), ntid) + global_id = builder.add(global_id, tid) + + # Index all pointer arguments. Ignore the thread count argument + args = list(kernel_func.args)[:-1] + indexed_args = [] + + # pointer args do not alias + for a in args: + if isinstance(a.type, ir.PointerType): + a.attributes.add('noalias') + + def _upload_to_shared(b, ptr, name): + shared = ir.GlobalVariable(module, ptr.type.pointee, + name=function.name + "_shared_" + name, + addrspace=3) + shared.alignment = 128 + shared.linkage = "internal" + shared_ptr = b.addrspacecast(shared, shared.type.pointee.as_pointer()) + + char_ptr_ty = ir.IntType(8).as_pointer() + bool_ty = ir.IntType(1) + + ptr_src = b.bitcast(ptr, char_ptr_ty) + ptr_dst = b.bitcast(shared_ptr, char_ptr_ty) + + obj_size_ty = ir.FunctionType(ir.IntType(32), [char_ptr_ty, bool_ty, bool_ty, bool_ty]) + obj_size_f = module.declare_intrinsic("llvm.objectsize.i32", [], obj_size_ty) + # the params are: obj pointer, 0 on unknown size, NULL is unknown, size at runtime + obj_size = b.call(obj_size_f, [ptr_dst, bool_ty(1), bool_ty(0), bool_ty(0)]) + + if "unaligned_copy" not in debug_env: + int_ty = ir.IntType(32) + int_ptr_ty = int_ty.as_pointer() + obj_size = builder.add(obj_size, obj_size.type((int_ty.width // 8) - 1)) + obj_size = builder.udiv(obj_size, obj_size.type(int_ty.width // 8)) + ptr_src = builder.bitcast(ptr, int_ptr_ty) + ptr_dst = builder.bitcast(shared_ptr, int_ptr_ty) + + + # copy data using as many threads as available in thread group + with helpers.for_loop(b, tid, obj_size, ntid, id="copy_" + name) as (b1, i): + src = b1.gep(ptr_src, [i]) + dst = b1.gep(ptr_dst, [i]) + b1.store(b1.load(src), dst) + + sync_threads_ty = ir.FunctionType(ir.VoidType(), []) + sync_threads = module.declare_intrinsic("llvm.nvvm.barrier0", [], sync_threads_ty) + builder.call(sync_threads, []) + + return b, shared_ptr + + if is_grid_ranged and "cuda_no_shared" not in debug_env: + builder, args[0] = _upload_to_shared(builder, args[0], "params") + builder, args[1] = _upload_to_shared(builder, args[1], "state") + builder, args[3] = _upload_to_shared(builder, args[3], "inputs") + builder, args[4] = _upload_to_shared(builder, args[4], "data") # Check global id and exit if we're over should_quit = builder.icmp_unsigned(">=", global_id, kernel_func.args[-1]) with builder.if_then(should_quit): builder.ret_void() - # Index all pointer arguments. Ignore the thread count argument - args = list(kernel_func.args)[:-1] - indexed_args = [] - # If we're calling ranged search there are no offsets if is_grid_ranged: next_id = builder.add(global_id, global_id.type(1)) @@ -527,6 +580,7 @@ def _gen_cuda_kernel_wrapper_module(function): arg = builder.gep(arg, [offset]) indexed_args.append(arg) + builder.call(decl_f, indexed_args) builder.ret_void() diff --git a/psyneulink/core/llvm/jit_engine.py b/psyneulink/core/llvm/jit_engine.py index c18146237ac..d3a6656b4c1 100644 --- a/psyneulink/core/llvm/jit_engine.py +++ b/psyneulink/core/llvm/jit_engine.py @@ -356,6 +356,6 @@ def get_kernel(self, name): self.stage_compilation({wrapper_mod}) self.compile_staged() kernel = self._engine._find_kernel(name + "_cuda_kernel") - kernel.set_cache_config(pycuda.driver.func_cache.PREFER_L1) +# kernel.set_cache_config(pycuda.driver.func_cache.PREFER_L1) return kernel From 96169320891c5f49bca829b88506418ee77c1e75 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 4 Feb 2022 01:53:05 -0500 Subject: [PATCH 131/285] llvm/cuda: Automatically calculate the 'best' block size Prefer large blocks if the kernel uses shared memory. Prefer warp sized blocks otherwise. Print kenel statistics and selected block size with PNL_LLVM_DEBUG=stat Signed-off-by: Jan Vesely --- psyneulink/core/llvm/__init__.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/llvm/__init__.py b/psyneulink/core/llvm/__init__.py index e971df3d616..f59a46e4dde 100644 --- a/psyneulink/core/llvm/__init__.py +++ b/psyneulink/core/llvm/__init__.py @@ -13,6 +13,7 @@ import functools import numpy as np import time +from math import ceil, log2 from typing import Set from llvmlite import ir @@ -129,12 +130,38 @@ def _cuda_kernel(self): self.__cuda_kernel = _ptx_engine.get_kernel(self.name) return self.__cuda_kernel - def cuda_call(self, *args, threads=1, block_size=128): + def cuda_max_block_size(self, override): + if override is not None: + return override + + kernel = self._cuda_kernel + device = jit_engine.pycuda.autoinit.device + + if kernel.shared_size_bytes > 0: + # we use shared memory, prefer big blocks. + # Limited by reg usage + rounded_regs = 2 ** ceil(log2(kernel.num_regs)) + block_size = device.get_attribute(jit_engine.pycuda.driver.device_attribute.MAX_REGISTERS_PER_BLOCK) // rounded_regs + else: + # Use smallest possible blocks + block_size = device.get_attribute(jit_engine.pycuda.driver.device_attribute.WARP_SIZE) + + block_size = min(device.get_attribute(jit_engine.pycuda.driver.device_attribute.MAX_THREADS_PER_BLOCK), block_size) + if "stat" in debug_env: + print("kernel '", self.name, "' registers:", kernel.num_regs) + print("kernel '", self.name, "' local memory size:", kernel.local_size_bytes) + print("kernel '", self.name, "' shared memory size:", kernel.shared_size_bytes) + print("kernel '", self.name, "' selected block size:", block_size) + + return block_size + + def cuda_call(self, *args, threads=1, block_size=None): + block_size = self.cuda_max_block_size(block_size) grid = ((threads + block_size - 1) // block_size, 1) self._cuda_kernel(*args, np.int32(threads), block=(block_size, 1, 1), grid=grid) - def cuda_wrap_call(self, *args, threads=1, block_size=128): + def cuda_wrap_call(self, *args, threads=1, block_size=None): wrap_args = (jit_engine.pycuda.driver.InOut(a) if isinstance(a, np.ndarray) else a for a in args) self.cuda_call(*wrap_args, threads=threads, block_size=block_size) From 1ae98af27ca25521fc598131986c7f8e0e6ee4e5 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 6 Feb 2022 23:33:28 -0500 Subject: [PATCH 132/285] llvm/cuda: Drop 'cuda_data' debug switch Print upload/download statistics when 'stat' switch is present. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/debug.py | 1 - psyneulink/core/llvm/execution.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/psyneulink/core/llvm/debug.py b/psyneulink/core/llvm/debug.py index 090ce8b775f..236d2343c98 100644 --- a/psyneulink/core/llvm/debug.py +++ b/psyneulink/core/llvm/debug.py @@ -19,7 +19,6 @@ * "compile" -- prints information messages when modules are compiled * "stat" -- prints code generation and compilation statistics * "time_stat" -- print compilation and code generation times - * "cuda_data" -- print data upload/download statistic (to GPU VRAM) * "comp_node_debug" -- print intermediate results after execution composition node wrapper. * "print_values" -- Enabled printfs in llvm code (from ctx printf helper) diff --git a/psyneulink/core/llvm/execution.py b/psyneulink/core/llvm/execution.py index ac722dd9d77..1f54c69072b 100644 --- a/psyneulink/core/llvm/execution.py +++ b/psyneulink/core/llvm/execution.py @@ -110,7 +110,7 @@ def __init__(self, buffers=['param_struct', 'state_struct', 'out']): self._downloaded_bytes = Counter() def __del__(self): - if "cuda_data" in self._debug_env: + if "stat" in self._debug_env: try: name = self._bin_func.name except AttributeError: From ba622dd34883a848f6edecc6badc99636c45b870 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Mon, 7 Feb 2022 13:30:23 -0500 Subject: [PATCH 133/285] llvm/debug: Rename IR/asm code dumping option "llvm" -> "dump-llvm-gen" "llvm-op" -> "dump-llvm-opt" "isa" -> "dump-llvm-asm" Rename .parses.ll -> .generated.ll Signed-off-by: Jan Vesely --- psyneulink/core/llvm/debug.py | 8 ++++---- psyneulink/core/llvm/jit_engine.py | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/psyneulink/core/llvm/debug.py b/psyneulink/core/llvm/debug.py index 236d2343c98..d9a0b3f0578 100644 --- a/psyneulink/core/llvm/debug.py +++ b/psyneulink/core/llvm/debug.py @@ -37,10 +37,10 @@ * "cuda_max_regs" -- Set maximum allowed GPU arch registers Compiled code dump: - * "llvm" -- dumps LLVM IR into a file (named after the dumped module). - Code is dumped both after module generation and linking into global module. - * "llvm-opt" -- dump LLVM IR after running through the optimization passes - * "isa" -- dump machine specific ISA + * "dump-llvm-gen" -- Dumps LLVM IR generated by us into a file (named after the dumped module). + IR is dumped both after module generation and linking into global module. + * "dump-llvm-opt" -- Dump LLVM IR after running through the optimization passes. + * "dump-asm" -- Dump machine specific asm, fed to the JIT engine (currently CPU ISA, or PTX). """ import os diff --git a/psyneulink/core/llvm/jit_engine.py b/psyneulink/core/llvm/jit_engine.py index d3a6656b4c1..3e81195b6ad 100644 --- a/psyneulink/core/llvm/jit_engine.py +++ b/psyneulink/core/llvm/jit_engine.py @@ -90,8 +90,8 @@ def _cpu_jit_constructor(): # And an execution engine with a builtins backing module builtins_module = _generate_cpu_builtins_module(LLVMBuilderContext.get_current().float_ty) - if "llvm" in debug_env: - with open(builtins_module.name + '.parse.ll', 'w') as dump_file: + if "dump-llvm-gen" in debug_env: + with open(builtins_module.name + '.generated.ll', 'w') as dump_file: dump_file.write(str(builtins_module)) __backing_mod = binding.parse_assembly(str(builtins_module)) @@ -126,8 +126,8 @@ def _ptx_jit_constructor(): def _try_parse_module(module): - if "llvm" in debug_env: - with open(module.name + '.parse.ll', 'w') as dump_file: + if "dump-llvm-gen" in debug_env: + with open(module.name + '.generated.ll', 'w') as dump_file: dump_file.write(str(module)) # IR module is not the same as binding module. @@ -177,12 +177,12 @@ def opt_and_add_bin_module(self, module): if "time_stat" in debug_env: print("Time to optimize LLVM module bundle '{}': {}".format(module.name, finish - start)) - if "llvm-opt" in self.__debug_env: + if "dump-llvm-opt" in self.__debug_env: with open(self.__class__.__name__ + '-' + str(self.__optimized_modules) + '.opt.ll', 'w') as dump_file: dump_file.write(str(module)) # This prints generated x86 assembly - if "isa" in self.__debug_env: + if "dump-asm" in self.__debug_env: with open(self.__class__.__name__ + '-' + str(self.__optimized_modules) + '.S', 'w') as dump_file: dump_file.write(self._target_machine.emit_assembly(module)) @@ -208,7 +208,7 @@ def opt_and_append_bin_module(self, module): self.__mod.link_in(module) self.__linked_modules += 1 - if "llvm" in debug_env: + if "dupm-llvm-gen" in debug_env: with open(mod_name + '.linked.ll', 'w') as dump_file: dump_file.write(str(self.__mod)) From 630ac7b525d004401d6f32fa1a60d8df8107280d Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Mon, 7 Feb 2022 13:42:13 -0500 Subject: [PATCH 134/285] llvm/debug: Document 'cuda_no_shared' option Create a separate category for CUDA related options. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/debug.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/llvm/debug.py b/psyneulink/core/llvm/debug.py index d9a0b3f0578..c261b9d57ca 100644 --- a/psyneulink/core/llvm/debug.py +++ b/psyneulink/core/llvm/debug.py @@ -34,7 +34,11 @@ instead of laoding them from the context argument * "opt" -- Set compiler optimization level (0,1,2,3) * "unaligned_copy" -- Do not assume structures are 4B aligned - * "cuda_max_regs" -- Set maximum allowed GPU arch registers + +CUDA options: + * "cuda_max_regs" -- Set maximum allowed GPU arch registers. + Equivalent to the CUDA JIT compiler option of the same name. + * "cuda_no_shared" -- Do not use on-chip 'shared' memory in generated code. Compiled code dump: * "dump-llvm-gen" -- Dumps LLVM IR generated by us into a file (named after the dumped module). From 39c7e0a6bf9e1bcb90b7cac35ccd70947084364e Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Mon, 7 Feb 2022 14:01:22 -0500 Subject: [PATCH 135/285] llvm/debug: Remove 'alloca_data' debug option. Not used. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/codegen.py | 13 +------------ psyneulink/core/llvm/debug.py | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index 8c5adcec20b..ca5d8d38818 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -711,19 +711,12 @@ def _gen_composition_exec_context(ctx, composition, *, tags:frozenset, suffix="" for a in llvm_func.args: a.attributes.add('noalias') - state, params, comp_in, data_arg, cond, *_ = llvm_func.args + state, params, comp_in, data, cond, *_ = llvm_func.args if "const_params" in debug_env: const_params = params.type.pointee(composition._get_param_initializer(None)) params = builder.alloca(const_params.type, name="const_params_loc") builder.store(const_params, params) - if "alloca_data" in debug_env: - data = builder.alloca(data_arg.type.pointee, name="data_loc") - data_vals = builder.load(data_arg) - builder.store(data_vals, data) - else: - data = data_arg - node_tags = tags.union({"node_wrapper"}) # Call input CIM input_cim_w = ctx.get_node_wrapper(composition, composition.input_CIM) @@ -737,10 +730,6 @@ def _gen_composition_exec_context(ctx, composition, *, tags:frozenset, suffix="" yield builder, data, params, cond_gen - if "alloca_data" in debug_env: - data_vals = builder.load(data) - builder.store(data_vals, data_arg) - # Bump run counter cond_gen.bump_ts(builder, cond, (1, 0, 0)) diff --git a/psyneulink/core/llvm/debug.py b/psyneulink/core/llvm/debug.py index c261b9d57ca..0a6788f838d 100644 --- a/psyneulink/core/llvm/debug.py +++ b/psyneulink/core/llvm/debug.py @@ -23,7 +23,6 @@ * "print_values" -- Enabled printfs in llvm code (from ctx printf helper) Compilation modifiers: - * "alloca_data" -- use alloca'd storage for composition data (exposes data flow) * "debug_info" -- emit line debugging information when generating LLVM IR * "const_data" -- hardcode initial output values into generated code, instead of loading them from the data argument From e5f9dc7c4de23488f5e8c8e7f4c84703719564f5 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Mon, 7 Feb 2022 11:46:00 -0500 Subject: [PATCH 136/285] setup: Check code coverage of blocks guarded by most debug options Do not check code guarded dump-* or compile options. test_debug_composition should cover most of theses codepaths. Signed-off-by: Jan Vesely --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 4669e2ba462..f4bb89a9ded 100644 --- a/setup.cfg +++ b/setup.cfg @@ -93,7 +93,8 @@ exclude_lines = # Don't complain about missing debug-only code: def __repr__ if self\.debug - if .* in .*debug_env: + if .*dump.* in .*debug_env: + if .*compile.* in .*debug_env: # Don't complain if tests don't hit defensive assertion code: raise .*Error From 7332ab5fe70b60174b80f58cd017e92efe8a25a0 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 8 Feb 2022 15:52:00 -0500 Subject: [PATCH 137/285] llvm/jit_engine: Fix typo in debug option check (#2313) Signed-off-by: Jan Vesely --- psyneulink/core/llvm/jit_engine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/llvm/jit_engine.py b/psyneulink/core/llvm/jit_engine.py index 3e81195b6ad..ce1b9a66e6f 100644 --- a/psyneulink/core/llvm/jit_engine.py +++ b/psyneulink/core/llvm/jit_engine.py @@ -208,7 +208,7 @@ def opt_and_append_bin_module(self, module): self.__mod.link_in(module) self.__linked_modules += 1 - if "dupm-llvm-gen" in debug_env: + if "dump-llvm-gen" in debug_env: with open(mod_name + '.linked.ll', 'w') as dump_file: dump_file.write(str(self.__mod)) From 82d43be8fc690e2456d6ce3d3fbb2cd9d193f3cd Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 9 Feb 2022 19:33:13 -0500 Subject: [PATCH 138/285] Feat/comp/get input format (#2315) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • composition.py: get_input_format(): add template option to return formatted dict for use as input to run() * - * - * • composition.py, mechanism.py: - add external_input_variables properties - add get_input_variables() methods replace use of input.value with input.varible for validating/sizing inputs * - * - * • function.py: Function_Base: - add input_shape_template attribute (as experiment for now) * • mechanism.py: - add: _input_shape_template, default_input_shape, input_shape, get_input_shape, default_external_input_shape, external_input_shape - _get_variable_from_input(): refactor to use above instead of variable or value * • mechanism.py, inputport.py: move from mechanism to inputport: _input_shape_template, default_input_shape, input_shape, get_input_shape * - * - * - * - * - * - * • mechanism.py: - get_variable_from_input(): - refactor to assign each item of input to corresponding input_port.variable and each input_port.value to result of executing its function with the variable * - * - * - * - * • Passes all tests * • Passes all tests • function.py: input_shape_template -> changes_shape * - * • test_composition.py: add: test_get_input_format() * - Before adding trials and labels to template to Composition.get_input_format() * - Before adding trials and labels to template to Composition.get_input_format() * - * - * - * - Before adding trials and labels to template to Composition.get_input_format() * - * - Before adding trials and labels to template to Composition.get_input_format() * - Before adding trials and labels to template to Composition.get_input_format() * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - get_input_format(): - add form option that returns dict formatted for use as **inputs** arg of run() method - support dict that specifies labels for entire mech, not ports individually - _parse_labels(): fix bugs to support above • utilities.py: - nested_depth(): returns depth of ragged lists and np.ndarrays • test_composition.py: - add test_get_input_format() Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- .../core/components/mechanisms/mechanism.py | 6 +- psyneulink/core/compositions/composition.py | 239 ++++++++++++------ psyneulink/core/globals/keywords.py | 10 +- psyneulink/core/globals/utilities.py | 60 +++-- tests/composition/test_composition.py | 116 ++++++--- tests/composition/test_control.py | 56 ++-- 6 files changed, 313 insertions(+), 174 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index f741f685de1..9fc6cbe3796 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -3973,7 +3973,7 @@ def output_port(self): @property def output_values(self): - return self.output_ports.values + return self.get_output_values() def get_output_values(self, context=None): return [output_port.parameters.value.get(context) for output_port in self.output_ports] @@ -3993,8 +3993,8 @@ def get_output_labels(self, context=None): elif context: return self.get_output_values(context) else: - return self.output_values - + # Use this to report most recent value if no context is available + return self.output_ports.values @property def ports(self): diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 4028d075254..27fe68383f8 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2640,7 +2640,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, ALLOW_PROBES, ANY, BEFORE, COMPONENT, COMPOSITION, CONTROL, CONTROL_SIGNAL, CONTROLLER, DEFAULT, \ - FEEDBACK, FUNCTION, HARD_CLAMP, IDENTITY_MATRIX, INPUT, INPUT_PORTS, INPUTS, INPUT_CIM_NAME, \ + DICT, FEEDBACK, FULL, FUNCTION, HARD_CLAMP, IDENTITY_MATRIX, INPUT, INPUT_PORTS, INPUTS, INPUT_CIM_NAME, \ LEARNED_PROJECTIONS, LEARNING_FUNCTION, LEARNING_MECHANISM, LEARNING_MECHANISMS, LEARNING_PATHWAY, \ MATRIX, MATRIX_KEYWORD_VALUES, MAYBE, \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, \ @@ -2649,14 +2649,14 @@ def input_function(env, result): OUTPUT, OUTPUT_CIM_NAME, OUTPUT_MECHANISM, OUTPUT_PORTS, OWNER_VALUE, \ PARAMETER, PARAMETER_CIM_NAME, PROCESSING_PATHWAY, PROJECTION, PROJECTION_TYPE, PROJECTION_PARAMS, PULSE_CLAMP, \ SAMPLE, SHADOW_INPUTS, SOFT_CLAMP, SSE, \ - TARGET, TARGET_MECHANISM, VARIABLE, WEIGHT, OWNER_MECH + TARGET, TARGET_MECHANISM, TEXT, VARIABLE, WEIGHT, OWNER_MECH from psyneulink.core.globals.log import CompositionLog, LogCondition from psyneulink.core.globals.parameters import Parameter, ParametersBase from psyneulink.core.globals.preferences.basepreferenceset import BasePreferenceSet from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel, _assign_prefs from psyneulink.core.globals.registry import register_category from psyneulink.core.globals.utilities import \ - ContentAddressableList, call_with_pruned_args, convert_to_list, convert_to_np_array, is_numeric + ContentAddressableList, call_with_pruned_args, convert_to_list, nesting_depth, convert_to_np_array, is_numeric from psyneulink.core.scheduling.condition import All, AllHaveRun, Always, Any, Condition, Never from psyneulink.core.scheduling.scheduler import Scheduler, SchedulingMode from psyneulink.core.scheduling.time import Time, TimeScale @@ -8565,12 +8565,13 @@ def _validate_input_shapes(self, inputs): node_input = [node_input] else: # if node_input is None, it may mean there are multiple trials of input in the stimulus set, - # so loop through and validate each individual input + # so in list comprehension below loop through and validate each individual input node_input = [self._validate_single_input(node, single_trial_input) for single_trial_input in stimulus] + # Look for any bad ones (for which _validate_single_input() returned None) and report if found if True in [i is None for i in node_input]: - # incompatible_stimulus = [stimulus[node_input.index(None)]] - incompatible_stimulus = np.atleast_1d(stimulus[node_input.index(None)]) - correct_stimulus = np.atleast_1d(node.external_input_shape[node_input.index(None)]) + incompatible_stimulus = np.atleast_1d(np.array(stimulus[node_input.index(None)], dtype=object)) + correct_stimulus = np.atleast_1d(np.array(node.external_input_shape[node_input.index(None)], + dtype=object)) node_name = node.name err_msg = f"Input stimulus ({incompatible_stimulus}) for {node_name} is incompatible with " \ f"the shape of its external input ({correct_stimulus})." @@ -8658,18 +8659,18 @@ def _parse_names_in_inputs(self, inputs): inputs[node] = inputs.pop(name) return inputs - def _parse_labels(self, inputs, mech=None, context=None): + def _parse_labels(self, inputs, mech=None, port=None, context=None): """ - Traverse input dict and replace any inputs that are in the form of their input or output label representations - to their numeric representations + Traverse input dict and resolve any input or output labels to their numeric values + If **port** is passed, inputs is only for a single port, so manage accordingly Returns ------- `dict` : - The input dict, with inputs with their label representations replaced by their numeric representations - + The input dict, with inputs with labels replaced by corresponding numeric values """ + # the nested list comp below is necessary to retrieve target nodes of learning pathways, # because the PathwayRole enum is not importable into this module target_to_output = {path.target: path.output for path in self.pathways @@ -8685,23 +8686,40 @@ def _parse_labels(self, inputs, mech=None, context=None): for k,v in inputs.items(): if isinstance(k, Mechanism) and \ (target_to_output[k].output_labels_dict if k in target_to_output else k.input_labels_dict): - _inputs.update({k:self._parse_labels(v, k)}) + _inputs.update({k:self._parse_labels(v, k)}) # Full node's worth of inputs, so don't pass port else: # Call _parse_labels for any Nodes with input_labels_dicts in nested Composition(s) if (isinstance(k, Composition) and any(n.input_labels_dict for n in k._get_nested_nodes_with_same_roles_at_all_levels(k,NodeRole.INPUT))): - for i, port in enumerate(k.input_CIM.input_ports): - _, mech_with_labels, __ = k.input_CIM._get_destination_info_from_input_CIM(port) - v[i] = k._parse_labels(inputs[k][i],mech_with_labels) + if nesting_depth(v) == 2: + # Enforce that even single trial specs user outer trial dimension (for consistency below) + v = [v] + for t in range(len(v)): + for i, cim_port in enumerate(k.input_CIM.input_ports): + port, mech_with_labels, __ = k.input_CIM._get_destination_info_from_input_CIM(cim_port) + # Get only a port's worth of input, so signify by passing port with input, + # which is also need since it is not bound to owning Mechanism in input_CIM, + # so its index can't be determined in recursive call to _parse_labels below + v[t][i] = k._parse_labels(v[t][i],mech_with_labels, port) _inputs.update({k:v}) else: _inputs.update({k:v}) elif type(inputs) == list or type(inputs) == np.ndarray: _inputs = [] - for i in range(len(inputs)): - port = 0 if len(labels) == 1 else i - stimulus = inputs[i] + for i in range(len(inputs)): # One for each port if full node's worth, else inputs = input for port + if port: + # port passed in, since it is not bound to owner in input_CIM, so index can't be determined locally + # also signifies input is to be treated as just for that port (not entire node), so 1 dim less + port_index = mech.input_ports.index(port) + if not isinstance(inputs[0], str): + # If input for port is not a string, no further processing need, so just return as is + _inputs = inputs + break + else: + # No port passed in, so use primary InputPort if only one input, or i if inputs for multiple ports + port_index = 0 if len(labels) == 1 else i + stimulus = inputs[i] # input for port if type(stimulus) == list or type(stimulus) == np.ndarray: _inputs.append(self._parse_labels(inputs[i], mech)) elif type(stimulus) == str: @@ -8709,12 +8727,16 @@ def _parse_labels(self, inputs, mech=None, context=None): raise CompositionError(f"Inappropriate use of str ({repr(stimulus)}) as a stimulus for " f"{mech.name} in {self.name}: it does not have an input_labels_dict.") try: - _inputs.append(labels[port][stimulus]) + if len(inputs) == 1: + _inputs = np.atleast_1d(labels[port_index][stimulus]) + else: + _inputs.append(labels[port_index][stimulus]) except KeyError as e: raise CompositionError(f"Inappropriate use of {repr(stimulus)} as a stimulus for {mech.name} " f"in {self.name}: it is not a label in its input_labels_dict.") else: _inputs.append(stimulus) + return _inputs def _parse_input_dict(self, inputs, context=None): @@ -10488,124 +10510,177 @@ def __call__(self, *args, **kwargs): bad_args_str = ", ".join([str(arg) for arg in args] + list(kwargs.keys())) raise CompositionError(f"Composition ({self.name}) called with illegal argument(s): {bad_args_str}") - # Alias of get_input_format(easy mistake to make) def get_inputs_format(self, **kwargs): return self.get_input_format(**kwargs, alias="get_inputs_format") def get_input_format(self, - num_trials:int=1, + form:Union[DICT,TEXT]=DICT, + num_trials:Union[int, FULL]=1, use_labels:bool=False, - show_nested_input_nodes:bool=False, - template:bool=False, use_names:bool=False, + show_nested_input_nodes:bool=False, alias:str=None): - """Return str with format of dict used by **inputs** argument of `run ` method. + """Return dict or string with format of dict used by **inputs** argument of `run ` method. Arguments --------- - num_trials : int : default 1 - specifies number of trials' worth of inputs to included in format. + form : DICT or TEXT : default DICT + specifies the form in which the exampple is returned; DICT (the default) returns a dict (with + **num_trials** worth of default values for each `INPUT ` `Node `) + formatted for use as the **inputs** arg of the Compositon's `run ` method; + TEXT returns a user-readable text description of the format (optionally with inputs required for + `INPUT ` `Nodes ` of any `nested Compositions ` + (see **show_nested_input_nodes** below). + + num_trials : int or FULL : default 1 + specifies number of trials' worth of inputs to include in returned item. If *FULL* is specified, + **use_labels** is automatically set to True, and **num_trials** is set to number of labels in the + `input_label_dict ` with the largest number of labels specified; if + none of the `INPUT ` Mechanisms in the Composition (including any nested ones) have an + `input_label_dict ` specified, **num_trials** is set to the default (1). use_labels : bool : default False - if True, show labels instead of values for Mechanisms that have an `input_label_dict + if True, shows labels instead of values for Mechanisms that have an `input_label_dict `. For **num_trials** = 1, a representative label is - shown; for **num_trials** > 1, use a different label for each trial shown, cycling - through the set if **num_trials** is greater than the number of labels. + shown; for **num_trials** > 1, a different label is used for each trial shown, cycling + through the set if **num_trials** is greater than the number of labels. If **num_trials = *FULL*, + trials will be included. + + it is set to the number of labels in the largest list specified in any `input_label_dict + ` specified for an `INPUT ` Mechanism; + + use_names : bool : default False + use `Node ` name as key for Node if **form** = DICT. show_nested_input_nodes : bool : default False show hierarchical display of `Nodes ` in `nested Compositions ` with names of destination `INPUT ` `Nodes ` and representative inputs, followed by the actual format used for the `run ` method. - template : bool : default False - return dict (with **num_trials** worth of default values for each `INPUT ` `Node - `) properly formatted for use inputs arg of `run ` method. + Returns + ------- - use_names : bool : default False - use `Node ` name as key for Node in template dict. - """ + Either a dict formatted appropriately for assignment as the **inputs** argument of the Composition's `run() + method (default), or string showing the format required by the **inputs** argument ` + (template_dict=False) - if template: - # Return dict that can be used as inputs arg to run() - input_dict = {} - for node in self.get_nodes_by_role(NodeRole.INPUT): - node_key = node.name if use_names else node - inputs_for_node = [port.default_input_shape for port in node.external_input_ports] - input_dict[node_key]=[inputs_for_node] * num_trials - return input_dict + """ if alias: warnings.warn(f"{alias} is aliased to get_input_format(); please use that in the future.") - def _get_inputs(comp, nesting_level=1, use_labels=False): + def _get_labels(labels_dict, index, input_port): + """Need index for InputPort, since owner Mechanism is not passed in.""" + + try: + return list(labels_dict[input_port.name].keys()) + except KeyError: + try: + return list(labels_dict[index].keys()) + except KeyError: + # Dict with no InputPort-specific subdicts, used to specify labels for all InputPorts of Mechanism + return list(labels_dict.keys()) + raise CompositionError(f"Unable to find labels for '{input_port.full_name}' of '{input_port.owner.name}'.") + + def _get_inputs(comp, nesting_level=1, use_labels=False, template_dict=str): - input_format = '' + format_description_string = '' indent = '\t' * nesting_level + template_input_dict = {} + for node in comp.get_nodes_by_role(NodeRole.INPUT): - input_format += '\n' + indent + node.name + ': ' + node_inputs_for_format_string = [] + format_description_string += '\n' + indent + node.name + ': ' + node_inputs_for_template_dict = [] + node_key = node.name if use_names else node # Nested Compositions if show_nested_input_nodes and isinstance(node, Composition): - trials = _get_inputs(node, nesting_level=nesting_level + 1, use_labels=use_labels) + # No need for node_inputs_for_template_dict here as template_dict never contains nested_input_nodes + node_inputs_for_format_string = _get_inputs(node, + nesting_level=nesting_level + 1, + use_labels=use_labels) - # Nested Composition else: - trials = [] for t in range(num_trials): + inputs_for_format = [] + inputs_for_template_dict = [] - # FIX: 2/3/22 - SHOULD REFACTOR TO USE InputPort.variable RATHER THAN input_values - # IN CASE AN InputPort'S FUNCTION CHANGES ITS SHAPE - # (SEE ABOVE FOR template) # Mechanism with labels if use_labels and isinstance(node, Mechanism) and node.input_labels_dict: - input_values = [] - for i in range(len(node.input_values)): - label_dict = node.input_labels_dict[i] - labels = list(label_dict.keys()) - input_values.append(repr(labels[t % len(labels)])) - trial = f"[{','.join(input_values)}]" + labels_dict = node.input_labels_dict + + for i in range(len(node.external_input_shape)): + labels = _get_labels(labels_dict, i, node.input_ports[i]) + inputs_for_format.append(repr(labels[t % len(labels)])) + inputs_for_template_dict.append(labels[t % len(labels)]) + trial = f"[{','.join(inputs_for_format)}]" # Mechanism(s) with labels in nested Compositions elif (use_labels and isinstance(node, Composition) and any(n.input_labels_dict for n in node._get_nested_nodes_with_same_roles_at_all_levels(node, NodeRole.INPUT))): - input_values = [] - for i, port in enumerate(node.input_CIM.input_ports): - _, mech, __ = node.input_CIM._get_destination_info_from_input_CIM(port) + + for port in node.input_CIM.input_ports: + input_port, mech, __ = node.input_CIM._get_destination_info_from_input_CIM(port) labels_dict = mech.input_labels_dict if labels_dict: - labels = list(labels_dict[0].keys()) - input_values.append(repr([labels[t % len(labels)]])) + labels = _get_labels(labels_dict, mech.input_ports.index(input_port), input_port) + inputs_for_format.append(repr([labels[t % len(labels)]])) + inputs_for_template_dict.append([labels[t % len(labels)]]) else: - input_values.append(repr(np.array(mech.input_values).tolist())) - trial = f"[{','.join(input_values)}]" + inputs_for_template_dict.append(port.default_input_shape) + inputs_for_format.append(repr(np.array(port.default_input_shape).tolist())) + trial = f"[{','.join(inputs_for_format)}]" # No Mechanism(s) with labels or use_labels == False else: - trial = f"[{','.join([repr(i.tolist()) for i in node.input_values])}]" + inputs_for_template_dict = [port.default_input_shape for port in node.external_input_ports] + trial = f"[{','.join([repr(i.tolist()) for i in inputs_for_template_dict])}]" - trials.append(trial) + node_inputs_for_format_string.append(trial) + node_inputs_for_template_dict.append(inputs_for_template_dict) - trials = ', '.join(trials) + node_inputs_for_format_string = ', '.join(node_inputs_for_format_string) if num_trials > 1: - trials = f"[ {trials} ]" + node_inputs_for_format_string = f"[ {node_inputs_for_format_string} ]" - input_format += trials + format_description_string += node_inputs_for_format_string if not show_nested_input_nodes: - input_format += ',' - nesting_level -= 1 - return input_format - - formatted_input = _get_inputs(self, 1, use_labels) - if show_nested_input_nodes: - preface = f"\nInputs to (nested) INPUT Nodes of {self.name} for {num_trials} trials:" - epilog = f"\n\nFormat as follows for inputs to run():\n" \ - f"{self.get_input_format(num_trials=num_trials)}" - return preface + formatted_input[:-1] + epilog - return '{' + formatted_input[:-1] + '\n}' + format_description_string += ',' + template_input_dict[node_key]=node_inputs_for_template_dict + nesting_level -= 1 + if form == DICT: + return template_input_dict + else: + return format_description_string + + if num_trials == FULL: + num_trials = 1 + # Get number of labels in largest list of any input_labels_dict in an INPUT Mechanism + for node in self._get_nested_nodes_with_same_roles_at_all_levels(self, NodeRole.INPUT): + if node.input_labels_dict: + labels_dict = node.input_labels_dict + num_trials = max(num_trials, max([len(labels) for labels in labels_dict.values()])) + + # Return dict usable for run() + if form == DICT: + show_nested_input_nodes = False + return _get_inputs(self, 1, use_labels, form) + # Return text format + else: + formatted_input = _get_inputs(self, 1, use_labels) + if show_nested_input_nodes: + preface = f"\nInputs to (nested) INPUT Nodes of {self.name} for {num_trials} trials:" + epilog = f"\n\nFormat as follows for inputs to run():\n" \ + f"{self.get_input_format(form=form, num_trials=num_trials)}" + return preface + formatted_input[:-1] + epilog + return '{' + formatted_input[:-1] + '\n}' + + # Aliases for get_results_by_node: def get_output_format(self, **kwargs): return self.get_results_by_nodes(**kwargs, alias="get_output_format") diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index 47d8ffaff8c..4cf413d5b10 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -41,7 +41,7 @@ 'CURRENT_EXECUTION_TIME', 'CUSTOM_FUNCTION', 'CYCLE', 'DDM_MECHANISM', 'DECAY', 'DEFAULT', 'DEFAULT_CONTROL_MECHANISM', 'DEFAULT_INPUT', 'DEFAULT_MATRIX', 'DEFAULT_PREFERENCE_SET_OWNER', 'DEFAULT_PROCESSING_MECHANISM', 'DEFAULT_VARIABLE', - 'DEFERRED_ASSIGNMENT', 'DEFERRED_DEFAULT_NAME', 'DEFERRED_INITIALIZATION', 'DictionaryMemory_FUNCTION', + 'DEFERRED_ASSIGNMENT', 'DEFERRED_DEFAULT_NAME', 'DEFERRED_INITIALIZATION', 'DICT', 'DictionaryMemory_FUNCTION', 'DIFFERENCE', 'DIFFERENCE', 'DIFFUSION', 'DIRECT', 'DISABLE', 'DISABLE_PARAM', 'DIST_FUNCTION_TYPE', 'DIST_MEAN', 'DIST_SHAPE', 'DISTANCE_FUNCTION', 'DISTANCE_METRICS', 'DISTRIBUTION_FUNCTION_TYPE', 'DIVISION', 'DRIFT_DIFFUSION_INTEGRATOR_FUNCTION', 'DRIFT_ON_A_SPHERE_INTEGRATOR_FUNCTION', 'DUAL_ADAPTIVE_INTEGRATOR_FUNCTION', @@ -111,8 +111,10 @@ 'SINGLETON', 'SIZE', 'SLOPE', 'SOFT_CLAMP', 'SOFTMAX_FUNCTION', 'SOURCE', 'SSE', 'STABILITY_FUNCTION', 'STANDARD_ARGS', 'STANDARD_DEVIATION', 'STANDARD_OUTPUT_PORTS', 'SUBTRACTION', 'SUM', 'TARGET', 'TARGET_MECHANISM', 'TARGET_LABELS_DICT', 'TERMINAL', 'TERMINATION_MEASURE', 'TERMINATION_THRESHOLD', - 'TERMINATION_COMPARISION_OP', 'TERSE', 'THRESHOLD', 'TIME', 'TIME_STEP_SIZE', 'TIME_STEPS_DIM', 'TRAINING_SET', - 'TRANSFER_FUNCTION_TYPE', 'TRANSFER_MECHANISM', 'TRANSFER_WITH_COSTS_FUNCTION', 'TRIAL', 'TRIALS_DIM', + 'TERMINATION_COMPARISION_OP', 'TERSE', 'TEXT', 'THRESHOLD', 'TIME', 'TIME_STEP_SIZE', 'TIME_STEPS_DIM', + 'TRAINING_SET', + 'TRANSFER_FUNCTION_TYPE', 'TRANSFER_MECHANISM', 'TRANSFER_WITH_COSTS_FUNCTION', + 'TRIAL', 'TRIALS_DIM', 'UNCHANGED', 'UNIFORM_DIST_FUNCTION', 'USER_DEFINED_FUNCTION', 'USER_DEFINED_FUNCTION_TYPE', 'VALUES', 'VALIDATE', 'VALIDATION', 'VALUE', 'VALUE_ASSIGNMENT', 'VALUE_FUNCTION', 'VARIABLE', 'VARIANCE', 'VECTOR', 'WALD_DIST_FUNCTION', 'WEIGHT', 'WEIGHTS', 'X_0', @@ -339,6 +341,8 @@ def _is_metric(metric): FULL = 'full' TERSE = 'terse' DIRECT = 'direct' +DICT = 'dict' +TEXT = 'text' LESS_THAN = '<' LESS_THAN_OR_EQUAL = '<=' diff --git a/psyneulink/core/globals/utilities.py b/psyneulink/core/globals/utilities.py index 6f09bb09485..73b8a682db3 100644 --- a/psyneulink/core/globals/utilities.py +++ b/psyneulink/core/globals/utilities.py @@ -67,6 +67,14 @@ * powerset * tensor_power +*LIST MANAGEMENT* +~~~~~~~~~~~~~~~~~ + +* `insert_list` +* `convert_to_list` +* `flatten_list` +* `nesting_depth` + *OTHER* ~~~~~~~ @@ -85,9 +93,6 @@ * `ContentAddressableList` * `make_readonly_property` * `get_class_attributes` -* `insert_list` -* `flatten_list` -* `convert_to_list` * `get_global_seed` * `set_global_seed` @@ -126,6 +131,7 @@ 'is_modulation_operation', 'is_numeric', 'is_numeric_or_none', 'is_number', 'is_same_function_spec', 'is_unit_interval', 'is_value_spec', 'kwCompatibilityLength', 'kwCompatibilityNumeric', 'kwCompatibilityType', + 'nesting_depth', 'make_readonly_property', 'Modulation', 'MODULATION_ADD', 'MODULATION_MULTIPLY','MODULATION_OVERRIDE', 'multi_getattr', 'np_array_less_than_2d', 'object_has_single_value', 'optional_parameter_spec', 'normpdf', @@ -665,6 +671,32 @@ def tensor_power(items, levels:tc.optional(range)=None, flat=False): return pp +# LIST MANAGEMENT ****************************************************************************************************** + +def insert_list(list1, position, list2): + """Insert list2 into list1 at position""" + return list1[:position] + list2 + list1[position:] + +def convert_to_list(l): + if l is None: + return None + elif isinstance(l, list): + return l + elif isinstance(l, ContentAddressableList): + return list(l) + elif isinstance(l, set): + return list(l) + else: + return [l] + +def flatten_list(l): + return [item for sublist in l for item in sublist] + +def nesting_depth(l): + if isinstance(l, np.ndarray): + l = l.tolist() + return isinstance(l, list) and max(map(nesting_depth, l)) + 1 + # OTHER **************************************************************************************************************** @@ -1524,28 +1556,6 @@ def convert_all_elements_to_np_array(arr, cast_from=None, cast_to=None): return elementwise_subarr - -def insert_list(list1, position, list2): - """Insert list2 into list1 at position""" - return list1[:position] + list2 + list1[position:] - - -def convert_to_list(l): - if l is None: - return None - elif isinstance(l, list): - return l - elif isinstance(l, ContentAddressableList): - return list(l) - elif isinstance(l, set): - return list(l) - else: - return [l] - -def flatten_list(l): - return [item for sublist in l for item in sublist] - - # Seeds and randomness class SeededRandomState(np.random.RandomState): diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 3333f650b55..6d7d18a3056 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -5746,39 +5746,89 @@ def inputs_generator_function(): else: assert ocomp.results[0:2] == ocomp.results[2:4] == ocomp.results[4:6] == [[-2], [100]] - def test_get_input_format(self): - A = ProcessingMechanism(size=1, name='A') - B = ProcessingMechanism(size=2, name='B') - C = ProcessingMechanism(size=[3,3], input_ports=['C INPUT 1', 'C INPUT 2'], name='C') + + expected_format_strings = \ + [ + '{\n\tX: [ [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ],' + '\n\tICOMP: [ [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ],' + '\n\tY: [ [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ]\n}', + + "\nInputs to (nested) INPUT Nodes of OCOMP for 3 trials:\n\tX: [ [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], [[0.0, " + "0.0, 0.0],[0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ]" + "\n\tICOMP: \n\t\tA: [ ['red'], ['green'], ['red'] ]" + "\n\t\tC: [ ['red','blue'], ['green','yellow'], ['orange','purple'] ]" + "\n\tY: [ ['red','red'], ['green','green'], ['red','red'] " + "\n\nFormat as follows for inputs to run():" + "\n{\n\tX: [ [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], " + "[[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], " + "[[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ]," + "\n\tICOMP: [ [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], " + "[[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], " + "[[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ]," + "\n\tY: [ [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], " + "[[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], " + "[[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ]\n}" + ] + test_args = [ + # form, labels, nested, num_trials, expected_format_string + (pnl.TEXT, False, False, 2, expected_format_strings[0]), + (pnl.TEXT, True, True, pnl.FULL, expected_format_strings[1]), + (pnl.DICT, False, False, 1, None), + (pnl.DICT, False, False, pnl.FULL, None), + (pnl.DICT, True, True, 1, None), + (pnl.DICT, True, False, pnl.FULL, None) + ] + @pytest.mark.parametrize('form, use_labels, show_nested, num_trials, expected_format_string', test_args, + ids = [f"{'dict' if x[0] else 'string'} " + f"{'use_labels_true' if x[1] else 'use_labels_false'} " + f"{'show_nested_true' if x[2] else 'show_nested_false'} " + f"num_trials-{x[3]} " + f"{'expected_format_string' if x[4] else 'None'}" for x in test_args] + ) + def test_get_input_format(self, form, use_labels, show_nested, num_trials, expected_format_string): + """Also tests input_labels_dict""" + + A = pnl.ProcessingMechanism(size=1, name='A', + input_labels={0:{'red':0, 'green':1}, + 1:{'blue':2, 'yellow':3}}) + B = pnl.ProcessingMechanism(size=2, name='B') + C = pnl.ProcessingMechanism(size=[3,3], + input_ports=['C INPUT 1', 'C INPUT 2'], + input_labels={'C INPUT 1':{'red':[0,0,0], 'green':[1,1,1], 'orange':[2,2,2]}, + 'C INPUT 2':{'blue':[3,3,3], 'yellow':[4,4,4], 'purple':[5,5,5]}}, + name='C') assert C.variable.shape == (2,3) - X = ProcessingMechanism(size=4, name='X') - Y = ProcessingMechanism(input_ports=[{NAME:'Y INPUT 1', pnl.SIZE: 3, pnl.FUNCTION: pnl.Reduce}], - name='Y') - assert len(Y.input_port.variable) == 3 - assert len(Y.input_port.value) == 1 + X = ProcessingMechanism(size=[3,3], + input_ports=['X INPUT 1', 'X INPUT 2'], + name='X', + # input_labels={0:{'red':[0,0,0], 'green':[1,1,1]}} # Specify dict for only one port + ) + # Use TransferMechanism so that 2nd OutputPort uses 2nd item of Mechanism's value + # (i.e. ,without having to specify that explicitly, as would be the case for ProcessingMechanism) + Y = pnl.TransferMechanism(input_ports=[{NAME:'Y INPUT 1', pnl.SIZE: 3, pnl.FUNCTION: pnl.Reduce}, + {NAME:'Y INPUT 2', pnl.SIZE: 3}], + # Test specification of labels for all InputPorts of Mechanism: + input_labels={'red':[0,0,0], 'green':[1,1,1]}, + name='Y') + assert len(Y.input_ports[0].variable) == 3 + assert len(Y.input_ports[0].value) == 1 + assert len(Y.input_ports[1].variable) == 3 + assert len(Y.input_ports[1].value) == 3 icomp = Composition(pathways=[[A,B],[C]], name='ICOMP') ocomp = Composition(nodes=[X, icomp, Y], name='OCOMP') - expected = '{\n\tX: [ [[0.0, 0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0, 0.0]] ],' \ - '\n\tICOMP: [ [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]], [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]] ],' \ - '\n\tY: [ [[0.0]], [[0.0]] ]\n}' - inputs_dict = ocomp.get_input_format(num_trials=2) - assert inputs_dict == expected - - expected = '\nInputs to (nested) INPUT Nodes of OCOMP for 1 trials:\n\tX: [[0.0, 0.0, 0.0, 0.0]]' \ - '\n\tICOMP: \n\t\tA: [[0.0]]\n\t\tC: [[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]]\n\tY: [[0.0]' \ - '\n\nFormat as follows for inputs to run():\n{\n\tX: [[0.0, 0.0, 0.0, 0.0]],' \ - '\n\tICOMP: [[0.0],[0.0, 0.0, 0.0],[0.0, 0.0, 0.0]],\n\tY: [[0.0]]\n}' - inputs_dict = ocomp.get_input_format(show_nested_input_nodes=True) - assert inputs_dict == expected - - inputs_dict = ocomp.get_input_format(template=True, use_names=False, num_trials=2) - ocomp.run(inputs=inputs_dict) - len(ocomp.results)==2 + inputs_dict = ocomp.get_input_format(form=form, + num_trials=num_trials, + use_labels=use_labels, + show_nested_input_nodes=show_nested) + if form == pnl.TEXT: + assert inputs_dict == expected_format_string + else: + ocomp.run(inputs=inputs_dict) + if num_trials == pnl.FULL: + num_trials = 2 + len(ocomp.results)==num_trials - inputs_dict = ocomp.get_input_format(template=True, use_names=True, num_trials=2) - ocomp.run(inputs=inputs_dict) - len(ocomp.results)==2 class TestProperties: @pytest.mark.composition @@ -6485,13 +6535,13 @@ def test_input_labels_and_results_by_node_and_no_orphaning_of_nested_output_node result = ocomp.run(inputs={mcomp:[[0],[0]]}) assert len(result)==4 - input_format = ocomp.get_input_format() + input_format = ocomp.get_input_format(form=pnl.TEXT) assert repr(input_format) == '\'{\\n\\tMIDDLE COMP: [[0.0],[0.0]],\\n\\tQ: [[0.0]]\\n}\'' - input_format = ocomp.get_input_format(num_trials=3, use_labels=True) - assert repr(input_format) == '"{\\n\\tMIDDLE COMP: [ [[[0.0]],[\'red\']], [[[0.0]],[\'green\']], [[[0.0]],[\'red\']] ],\\n\\tQ: [ [\'red\'], [\'green\'], [\'red\'] ]\\n}"' - input_format = ocomp.get_input_format(num_trials=2, show_nested_input_nodes=True) + input_format = ocomp.get_input_format(form=pnl.TEXT, num_trials=3, use_labels=True) + assert repr(input_format) == '"{\\n\\tMIDDLE COMP: [ [[0.0],[\'red\']], [[0.0],[\'green\']], [[0.0],[\'red\']] ],\\n\\tQ: [ [\'red\'], [\'green\'], [\'red\'] ]\\n}"' + input_format = ocomp.get_input_format(form=pnl.TEXT, num_trials=2, show_nested_input_nodes=True) assert input_format == '\nInputs to (nested) INPUT Nodes of OUTER COMP for 2 trials:\n\tMIDDLE COMP: \n\t\tX: [ [[0.0]], [[0.0]] ]\n\t\tINNER COMP: \n\t\t\tA: [ [[0.0]], [[0.0]] ]\n\tQ: [ [[0.0]], [[0.0]] \n\nFormat as follows for inputs to run():\n{\n\tMIDDLE COMP: [ [[0.0],[0.0]], [[0.0],[0.0]] ],\n\tQ: [ [[0.0]], [[0.0]] ]\n}' - input_format = ocomp.get_input_format(num_trials=2, show_nested_input_nodes=True, use_labels=True) + input_format = ocomp.get_input_format(form=pnl.TEXT, num_trials=2, show_nested_input_nodes=True, use_labels=True) assert input_format == "\nInputs to (nested) INPUT Nodes of OUTER COMP for 2 trials:\n\tMIDDLE COMP: \n\t\tX: [ [[0.0]], [[0.0]] ]\n\t\tINNER COMP: \n\t\t\tA: [ ['red'], ['green'] ]\n\tQ: [ ['red'], ['green'] \n\nFormat as follows for inputs to run():\n{\n\tMIDDLE COMP: [ [[0.0],[0.0]], [[0.0],[0.0]] ],\n\tQ: [ [[0.0]], [[0.0]] ]\n}" result = ocomp.run(inputs={mcomp:[[.2],['green']], Q:[4.6]}) diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index b22e2a689a6..d23e6021654 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -800,12 +800,12 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c f'that are missing from \'OUTER COMP\' and any Compositions nested within it."', # 4 - f"The 'state_features' argument has been specified for 'OptimizationControlMechanism-0' that is using a " - f"Composition ('OUTER COMP') as its agent_rep, but they are not compatible with the inputs required by " - f"its 'agent_rep': 'Input stimulus ([0.]) for OB is incompatible with the shape of its external input " - f"([0. 0. 0.]).' Use the get_inputs_format() method of 'OUTER COMP' to see the required format, or " - f"remove the specification of 'state_features' from the constructor for OptimizationControlMechanism-0 " - f"to have them automatically assigned.", + "The 'state_features' argument has been specified for 'OptimizationControlMechanism-0' that is using a " + "Composition ('OUTER COMP') as its agent_rep, but they are not compatible with the inputs required by " + "its 'agent_rep': 'Input stimulus ([0.0]) for OB is incompatible with the shape of its external input " + "([0.0 0.0 0.0]).' Use the get_inputs_format() method of 'OUTER COMP' to see the required format, or " + "remove the specification of 'state_features' from the constructor for OptimizationControlMechanism-0 " + "to have them automatically assigned.", # 5 f"The number of '{pnl.STATE_FEATURES}' specified for OptimizationControlMechanism-0 (4) is more than " @@ -845,29 +845,29 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ] state_feature_args = [ - ('partial_legal_list_spec', messages[0], None, UserWarning), - ('full_list_spec', None, None, None), - ('list_spec_with_none', None, None, None), - ('input_dict_spec', None, None, None), - ('input_dict_spec_short', None, None, None), - ('set_spec', None, None, None), - ('set_spec_short', None, None, None), - ('automatic_assignment', None, None, None), - ('shadow_inputs_dict_spec', None, None, None), - ('shadow_inputs_dict_spec_w_none', None, None, None), - ('misplaced_shadow', messages[1], None, pnl.CompositionError), - ('ext_shadow', messages[2], None, pnl.OptimizationControlMechanismError), - ('ext_output_port', messages[3], None, pnl.OptimizationControlMechanismError), + # ('partial_legal_list_spec', messages[0], None, UserWarning), + # ('full_list_spec', None, None, None), + # ('list_spec_with_none', None, None, None), + # ('input_dict_spec', None, None, None), + # ('input_dict_spec_short', None, None, None), + # ('set_spec', None, None, None), + # ('set_spec_short', None, None, None), + # ('automatic_assignment', None, None, None), + # ('shadow_inputs_dict_spec', None, None, None), + # ('shadow_inputs_dict_spec_w_none', None, None, None), + # ('misplaced_shadow', messages[1], None, pnl.CompositionError), + # ('ext_shadow', messages[2], None, pnl.OptimizationControlMechanismError), + # ('ext_output_port', messages[3], None, pnl.OptimizationControlMechanismError), ('input_format_wrong_shape', messages[4], None, pnl.OptimizationControlMechanismError), - ('too_many_inputs_warning', messages[5], None, UserWarning), - ('too_many_w_node_not_in_composition_warning', messages[6], None, UserWarning), - ('too_many_inputs_error', messages[7], None, pnl.OptimizationControlMechanismError), - ('bad_dict_spec_warning', messages[8], None, UserWarning), - ('bad_dict_spec_error', messages[8], None, pnl.OptimizationControlMechanismError), - ('bad_set_spec_warning', messages[0], messages[9], UserWarning), - ('bad_set_spec_error', messages[9], None, pnl.OptimizationControlMechanismError), - ('comp_in_list_spec', messages[10], None, pnl.OptimizationControlMechanismError), - ('comp_in_shadow_inupts_spec', messages[11], None, pnl.OptimizationControlMechanismError) + # ('too_many_inputs_warning', messages[5], None, UserWarning), + # ('too_many_w_node_not_in_composition_warning', messages[6], None, UserWarning), + # ('too_many_inputs_error', messages[7], None, pnl.OptimizationControlMechanismError), + # ('bad_dict_spec_warning', messages[8], None, UserWarning), + # ('bad_dict_spec_error', messages[8], None, pnl.OptimizationControlMechanismError), + # ('bad_set_spec_warning', messages[0], messages[9], UserWarning), + # ('bad_set_spec_error', messages[9], None, pnl.OptimizationControlMechanismError), + # ('comp_in_list_spec', messages[10], None, pnl.OptimizationControlMechanismError), + # ('comp_in_shadow_inupts_spec', messages[11], None, pnl.OptimizationControlMechanismError) ] @pytest.mark.control From c84a66af5d14dbcf34b90fae34d2e9de3530040d Mon Sep 17 00:00:00 2001 From: jdcpni Date: Thu, 10 Feb 2022 21:23:41 -0500 Subject: [PATCH 139/285] Fix/misc/jdc (#2316) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * • composition.py: - get_input_format: correct handling of failure to find labels • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors(): reinstate all conditions Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- psyneulink/core/compositions/composition.py | 10 +++-- tests/composition/test_control.py | 44 ++++++++++----------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 27fe68383f8..3905bd62f19 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -1088,7 +1088,7 @@ the `default_variable ` for each `INPUT` Node is used as its input on `TRIAL `. If it is not specified for the `learn ` method, an error is generated unless its **targets** argument is specified (see `below `). The Composition's `get_input_format() -` method can be used to show a template for how inputs should be formatted for the +` method can be used to show an example for how inputs should be formatted for the Composition, as well as the `INPUT ` Nodes to which they are assigned. The formats are described in more detail below. @@ -10563,8 +10563,8 @@ def get_input_format(self, ------- Either a dict formatted appropriately for assignment as the **inputs** argument of the Composition's `run() - method (default), or string showing the format required by the **inputs** argument ` - (template_dict=False) + method (form = *DICT*, the default), or string showing the format required by the **inputs** argument + ` (form = *TEXT*). """ @@ -10582,7 +10582,9 @@ def _get_labels(labels_dict, index, input_port): except KeyError: # Dict with no InputPort-specific subdicts, used to specify labels for all InputPorts of Mechanism return list(labels_dict.keys()) - raise CompositionError(f"Unable to find labels for '{input_port.full_name}' of '{input_port.owner.name}'.") + except: + assert False, f"PROGRAM ERROR: Unable to find labels for " \ + f"'{input_port.full_name}' of '{input_port.owner.name}'." def _get_inputs(comp, nesting_level=1, use_labels=False, template_dict=str): diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index d23e6021654..99287018c91 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -845,29 +845,29 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ] state_feature_args = [ - # ('partial_legal_list_spec', messages[0], None, UserWarning), - # ('full_list_spec', None, None, None), - # ('list_spec_with_none', None, None, None), - # ('input_dict_spec', None, None, None), - # ('input_dict_spec_short', None, None, None), - # ('set_spec', None, None, None), - # ('set_spec_short', None, None, None), - # ('automatic_assignment', None, None, None), - # ('shadow_inputs_dict_spec', None, None, None), - # ('shadow_inputs_dict_spec_w_none', None, None, None), - # ('misplaced_shadow', messages[1], None, pnl.CompositionError), - # ('ext_shadow', messages[2], None, pnl.OptimizationControlMechanismError), - # ('ext_output_port', messages[3], None, pnl.OptimizationControlMechanismError), + ('partial_legal_list_spec', messages[0], None, UserWarning), + ('full_list_spec', None, None, None), + ('list_spec_with_none', None, None, None), + ('input_dict_spec', None, None, None), + ('input_dict_spec_short', None, None, None), + ('set_spec', None, None, None), + ('set_spec_short', None, None, None), + ('automatic_assignment', None, None, None), + ('shadow_inputs_dict_spec', None, None, None), + ('shadow_inputs_dict_spec_w_none', None, None, None), + ('misplaced_shadow', messages[1], None, pnl.CompositionError), + ('ext_shadow', messages[2], None, pnl.OptimizationControlMechanismError), + ('ext_output_port', messages[3], None, pnl.OptimizationControlMechanismError), ('input_format_wrong_shape', messages[4], None, pnl.OptimizationControlMechanismError), - # ('too_many_inputs_warning', messages[5], None, UserWarning), - # ('too_many_w_node_not_in_composition_warning', messages[6], None, UserWarning), - # ('too_many_inputs_error', messages[7], None, pnl.OptimizationControlMechanismError), - # ('bad_dict_spec_warning', messages[8], None, UserWarning), - # ('bad_dict_spec_error', messages[8], None, pnl.OptimizationControlMechanismError), - # ('bad_set_spec_warning', messages[0], messages[9], UserWarning), - # ('bad_set_spec_error', messages[9], None, pnl.OptimizationControlMechanismError), - # ('comp_in_list_spec', messages[10], None, pnl.OptimizationControlMechanismError), - # ('comp_in_shadow_inupts_spec', messages[11], None, pnl.OptimizationControlMechanismError) + ('too_many_inputs_warning', messages[5], None, UserWarning), + ('too_many_w_node_not_in_composition_warning', messages[6], None, UserWarning), + ('too_many_inputs_error', messages[7], None, pnl.OptimizationControlMechanismError), + ('bad_dict_spec_warning', messages[8], None, UserWarning), + ('bad_dict_spec_error', messages[8], None, pnl.OptimizationControlMechanismError), + ('bad_set_spec_warning', messages[0], messages[9], UserWarning), + ('bad_set_spec_error', messages[9], None, pnl.OptimizationControlMechanismError), + ('comp_in_list_spec', messages[10], None, pnl.OptimizationControlMechanismError), + ('comp_in_shadow_inupts_spec', messages[11], None, pnl.OptimizationControlMechanismError) ] @pytest.mark.control From b1a9e6e41a0df9a6cbc49ff441f95c18a08f9bf5 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 8 Feb 2022 01:19:45 -0500 Subject: [PATCH 140/285] llvm, tests: Add test modulating DDM mechanism PRNG seed. Signed-off-by: Jan Vesely --- tests/composition/test_control.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 99287018c91..22b1dd85161 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1907,7 +1907,9 @@ def test_modulation_of_random_state_DDM(self, comp_mode, benchmark, prng): comp.add_node(mech, required_roles=pnl.NodeRole.INPUT) comp.add_node(ctl_mech) + # Seeds are chosen to show difference in results below. seeds = [13, 13, 14] + # cycle over the seeds twice setting and resetting the random state benchmark(comp.run, inputs={ctl_mech:seeds, mech:5.0}, num_trials=len(seeds) * 2, execution_mode=comp_mode) @@ -1916,6 +1918,35 @@ def test_modulation_of_random_state_DDM(self, comp_mode, benchmark, prng): elif prng == 'Philox': assert np.allclose(np.squeeze(comp.results[:len(seeds) * 2]), [[100, 19], [100, 21], [100, 21]] * 2) + @pytest.mark.benchmark + @pytest.mark.control + @pytest.mark.composition + # 'LLVM' mode is not supported, because synchronization of compiler and + # python values during execution is not implemented. + @pytest.mark.usefixtures("comp_mode_no_llvm") + @pytest.mark.parametrize('prng', ['Default', 'Philox']) + def test_modulation_of_random_state_DDM_Analytical(self, comp_mode, benchmark, prng): + # set explicit seed to make sure modulation is different + mech = pnl.DDM(function=pnl.DriftDiffusionAnalytical()) + if prng == 'Philox': + mech.parameters.random_state.set(_SeededPhilox([0])) + ctl_mech = pnl.ControlMechanism(control_signals=pnl.ControlSignal(modulates=('seed', mech), + modulation=pnl.OVERRIDE)) + comp = pnl.Composition() + comp.add_node(mech, required_roles=pnl.NodeRole.INPUT) + comp.add_node(ctl_mech) + + # Seeds are chosen to show difference in results below. + seeds = [3, 3, 4] + + # cycle over the seeds twice setting and resetting the random state + benchmark(comp.run, inputs={ctl_mech:seeds, mech:0.1}, num_trials=len(seeds) * 2, execution_mode=comp_mode) + + if prng == 'Default': + assert np.allclose(np.squeeze(comp.results[:len(seeds) * 2]), [[-1, 3.99948962], [1, 3.99948962], [-1, 3.99948962]] * 2) + elif prng == 'Philox': + assert np.allclose(np.squeeze(comp.results[:len(seeds) * 2]), [[-1, 3.99948962], [-1, 3.99948962], [1, 3.99948962]] * 2) + @pytest.mark.control @pytest.mark.composition @pytest.mark.parametrize("num_generators", [5]) From 324d250187feec4944e94b0e8625c9fa181cf885 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Thu, 10 Feb 2022 22:05:03 -0500 Subject: [PATCH 141/285] tests/mechanism: Update overlooked old way of selecting mech execution mode Pointed out by linter. Signed-off-by: Jan Vesely --- tests/mechanisms/test_transfer_mechanism.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/mechanisms/test_transfer_mechanism.py b/tests/mechanisms/test_transfer_mechanism.py index 34ffa016e2e..560de7fe8fb 100644 --- a/tests/mechanisms/test_transfer_mechanism.py +++ b/tests/mechanisms/test_transfer_mechanism.py @@ -967,12 +967,7 @@ def test_transfer_mech_integration_rate_0_8_initial_0_5(self, mech_mode): T.noise.base = 10 - if mech_mode == 'Python': - val = T.execute([1, 2, -3, 0]) - elif mech_mode == 'LLVM': - val = e.execute([1, 2, -3, 0]) - elif mech_mode == 'PTX': - val = e.cuda_execute([1, 2, -3, 0]) + val = EX([1, 2, -3, 0]) assert np.allclose(val, [[10.98, 11.78, 7.779999999999999, 10.18]]) # testing noise changes to an integrator # @pytest.mark.mechanism From f8afc168a9ed6d7efcf9a2116e87a70fd181571c Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 11 Feb 2022 00:42:55 -0500 Subject: [PATCH 142/285] tests/mechanism: Add compiled mode testing to integration_rate_0_8_list test Signed-off-by: Jan Vesely --- tests/mechanisms/test_transfer_mechanism.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/mechanisms/test_transfer_mechanism.py b/tests/mechanisms/test_transfer_mechanism.py index 560de7fe8fb..e6a295ce05a 100644 --- a/tests/mechanisms/test_transfer_mechanism.py +++ b/tests/mechanisms/test_transfer_mechanism.py @@ -988,17 +988,19 @@ def test_transfer_mech_integration_rate_0_8_initial_0_5(self, mech_mode): # ) @pytest.mark.mechanism @pytest.mark.transfer_mechanism - def test_transfer_mech_integration_rate_0_8_list(self): + def test_transfer_mech_integration_rate_0_8_list(self, mech_mode): T = TransferMechanism( name='T', default_variable=[0, 0, 0, 0], function=Linear(), - integration_rate=[0.8, 0.8, 0.8, 0.8], + integration_rate=[0.8, 0.7, 0.6, 0.5], integrator_mode=True ) - T.execute([1, 1, 1, 1]) - val = T.execute([1, 1, 1, 1]) - assert np.allclose(val, [[ 0.96, 0.96, 0.96, 0.96]]) + EX = pytest.helpers.get_mech_execution(T, mech_mode) + + EX([1, 1, 1, 1]) + val = EX([1, 1, 1, 1]) + assert np.allclose(val, [[ 0.96, 0.91, 0.84, 0.75]]) @pytest.mark.mechanism From 6cfb5c9464b937e5a12c13b65c0ffa994f583bda Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 12 Feb 2022 20:42:49 -0500 Subject: [PATCH 143/285] tests/mechanism: Switch test_with_contentaddressablememory to the new way of selecting execution mode Signed-off-by: Jan Vesely --- tests/mechanisms/test_episodic_memory.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/mechanisms/test_episodic_memory.py b/tests/mechanisms/test_episodic_memory.py index b4d02760546..ab27e385c9a 100644 --- a/tests/mechanisms/test_episodic_memory.py +++ b/tests/mechanisms/test_episodic_memory.py @@ -209,15 +209,12 @@ def test_with_contentaddressablememory(name, func, func_params, mech_params, tes assert em.input_ports.names == input_port_names assert em.output_ports.names == output_port_names - if mech_mode == 'Python': - def EX(variable): - em.execute(variable) - return em.output_values - elif mech_mode == 'LLVM': - pytest.skip("LLVM not yet implemented for ContentAddressableMemory") - elif mech_mode == 'PTX': + if mech_mode != 'Python': pytest.skip("PTX not yet implemented for ContentAddressableMemory") + EX = pytest.helpers.get_mech_execution(em, mech_mode) + + # EX(test_var) actual_output = EX(test_var) for i,j in zip(actual_output,expected_output): From 2aa2607cad14c33b8700592973707ee4e60882a7 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 12 Feb 2022 20:54:54 -0500 Subject: [PATCH 144/285] tests/llvm: Use 'def' instead named lambda Signed-off-by: Jan Vesely --- tests/llvm/test_builtins_matrix.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/llvm/test_builtins_matrix.py b/tests/llvm/test_builtins_matrix.py index 5660fbc862a..8010f3d317c 100644 --- a/tests/llvm/test_builtins_matrix.py +++ b/tests/llvm/test_builtins_matrix.py @@ -9,6 +9,7 @@ DIM_X = 1000 DIM_Y = 2000 u = np.random.rand(DIM_X, DIM_Y) +trans_u = u.transpose() v = np.random.rand(DIM_X, DIM_Y) vector = np.random.rand(DIM_X) trans_vector = np.random.rand(DIM_Y) @@ -24,7 +25,7 @@ mat_sub_res = np.subtract(u,v) mat_mul_res = np.multiply(u, v) dot_res = np.dot(vector, u) -trans_dot_res = np.dot(trans_vector, u.transpose()) +trans_dot_res = np.dot(trans_vector, trans_u) mat_sadd_res = np.add(u, scalar) mat_smul_res = np.multiply(u, scalar) @@ -95,7 +96,8 @@ def ex(): @pytest.mark.benchmark(group="Dot") def test_dot(benchmark, func_mode): if func_mode == 'Python': - ex = lambda : np.dot(vector, u) + def ex(): + return np.dot(vector, u) elif func_mode == 'LLVM': bin_f = pnlvm.LLVMBinaryFunction.get("__pnl_builtin_vxm") def ex(): @@ -153,8 +155,8 @@ def test_dot_llvm_constant_dim(benchmark, mode): @pytest.mark.benchmark(group="Dot") def test_dot_transposed(benchmark, func_mode): if func_mode == 'Python': - trans_u = u.transpose() - ex = lambda : np.dot(trans_vector, trans_u) + def ex(): + return np.dot(trans_vector, trans_u) elif func_mode == 'LLVM': bin_f = pnlvm.LLVMBinaryFunction.get("__pnl_builtin_vxm_transposed") def ex(): From 63316db755db8e5848133bf86766b973acf01db6 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 12 Feb 2022 21:07:17 -0500 Subject: [PATCH 145/285] tests/llvm/builtins: Use '1.0' instead of '1' to get reciprocal The operations are fast so avoiding int->float conversion saves ~15% of time. Signed-off-by: Jan Vesely --- tests/llvm/test_builtins_intrinsics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/llvm/test_builtins_intrinsics.py b/tests/llvm/test_builtins_intrinsics.py index 37d0204656b..dad65836dd8 100644 --- a/tests/llvm/test_builtins_intrinsics.py +++ b/tests/llvm/test_builtins_intrinsics.py @@ -13,8 +13,8 @@ (np.log, (x,), "__pnl_builtin_log", np.log(x)), (np.power, (x,y), "__pnl_builtin_pow", np.power(x, y)), (np.tanh, (x,), "__pnl_builtin_tanh", np.tanh(x)), - (lambda x: 1 / np.tanh(x), (x,), "__pnl_builtin_coth", 1 / np.tanh(x)), - (lambda x: 1 / np.sinh(x), (x,), "__pnl_builtin_csch", 1 / np.sinh(x)), + (lambda x: 1.0 / np.tanh(x), (x,), "__pnl_builtin_coth", 1 / np.tanh(x)), + (lambda x: 1.0 / np.sinh(x), (x,), "__pnl_builtin_csch", 1 / np.sinh(x)), ], ids=["EXP", "LOG", "POW", "TANH", "COTH", "CSCH"]) def test_builtin_op(benchmark, op, args, builtin, result, func_mode): if func_mode == 'Python': From ef7df0d46ec06785c735ba2002d7a6b70f0a93a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Feb 2022 02:15:46 +0000 Subject: [PATCH 146/285] requirements: update pytest requirement from <7.0.1 to <7.0.2 (#2318) --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index ffb5ee405e0..a3474d55688 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,5 +1,5 @@ jupyter<=1.0.0 -pytest<7.0.1 +pytest<7.0.2 pytest-benchmark<3.4.2 pytest-cov<3.0.1 pytest-helpers-namespace<2021.12.30 From 801f40c0e5bd6a41df45b88b509a4c2adb8e0e6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Feb 2022 04:33:00 +0000 Subject: [PATCH 147/285] requirements: update sphinx-autodoc-typehints requirement (#2319) --- doc_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_requirements.txt b/doc_requirements.txt index 9ed5718328f..53b11bf040e 100644 --- a/doc_requirements.txt +++ b/doc_requirements.txt @@ -1,3 +1,3 @@ psyneulink-sphinx-theme<1.2.3.1 sphinx<4.2.1 -sphinx_autodoc_typehints<1.17.0 +sphinx_autodoc_typehints<1.18.0 From 9cc7c34b75153c8b34170ff114ebe19987b8b7fa Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Mon, 14 Feb 2022 19:45:57 -0500 Subject: [PATCH 148/285] llvm/component: Drop more unused parameters from compiled structures Reduces size of DDM mech parameters: 1296B -> 520B. Reduces size of LCA mech parameters: 2552B -> 984B. Overall it reduces the size of parameter structure for predator-prey: 17kB -> 7.3kB stability-flexibility: 21kB -> 8kB Signed-off-by: Jan Vesely fixup drop params LCA: 984 DDM: 520 total: s/f: 8kB p/p: 7.3kB --- psyneulink/core/components/component.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index fa1207ce08e..b7c22230a1b 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1375,6 +1375,8 @@ def _get_compilation_params(self): "previous_w", "random_state", "is_finished_flag", "num_executions_before_finished", "num_executions", "variable", "value", "saved_values", "saved_samples", + "integrator_function_value", "termination_measure_value", + "execution_count", # Invalid types "input_port_variables", "results", "simulation_results", "monitor_for_control", "state_feature_values", "simulation_ids", @@ -1389,12 +1391,20 @@ def _get_compilation_params(self): # autodiff specific types "pytorch_representation", "optimizer", # duplicate - "allocation_samples", "control_allocation_search_space"} + "allocation_samples", "control_allocation_search_space", + # not used in computation + "has_recurrent_input_port", "enable_learning", + "enable_output_type_conversion", "changes_shape", + "output_type", 'bounds' } # Mechanism's need few extra entires: # * matrix -- is never used directly, and is flatened below # * integration rate -- shape mismatch with param port input if hasattr(self, 'ports'): blacklist.update(["matrix", "integration_rate"]) + else: + # Execute until finished is only used by mechanisms + blacklist.update(["execute_until_finished", "max_executions_before_finished"]) + def _is_compilation_param(p): if p.name not in blacklist and not isinstance(p, (ParameterAlias, SharedParameter)): #FIXME: this should use defaults From 74ffad90e8cec48caa652703a4eb1ae76adece9f Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Mon, 14 Feb 2022 23:03:34 -0500 Subject: [PATCH 149/285] llvm, port: Do not create copy of port parameters for OVERRIDE modulation It's not needed as the port function is not executed in OVERRIDE case. Signed-off-by: Jan Vesely --- psyneulink/core/components/ports/port.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index baf9e9e2353..e3b125df8a2 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -2289,10 +2289,10 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, base_params = pnlvm.helpers.get_param_ptr(builder, self, params, "function") - if len(self.mod_afferents) > 0: - # Create a local copy of the function parameters - # only if there are modulating projections - # LLVM is not eliminating the redundant copy + if any(a.sender.modulation != OVERRIDE for a in self.mod_afferents): + # Create a local copy of the function parameters only if + # there are modulating projections of type other than OVERRIDE. + # LLVM is not eliminating the redundant copy. f_params = builder.alloca(port_f.args[0].type.pointee, name="modulated_port_params") builder.store(builder.load(base_params), f_params) From dbe6a4c28f3a820ee2be3db588df57a87c5254b0 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 15 Feb 2022 18:23:49 -0500 Subject: [PATCH 150/285] Component: use shared_type copying for parameter spec (#2322) --- psyneulink/core/components/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index b7c22230a1b..c3ae9d3d1f9 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -2074,7 +2074,7 @@ def _is_user_specified(parameter): for p in filter(lambda x: not isinstance(x, (ParameterAlias, SharedParameter)), self.parameters._in_dependency_order): # copy spec so it is not overwritten later # TODO: check if this is necessary - p.spec = copy_parameter_value(p.spec) + p.spec = copy_parameter_value(p.spec, shared_types=shared_types) # set default to None context to ensure it exists if ( From a58b1e5b8a91bff104f52a4d7e126fc1e34e8f9b Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 16 Feb 2022 08:02:18 -0500 Subject: [PATCH 151/285] Refactor/ocm/state features param (#2323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute * • optimizationcontrolmechanism.py: added feature_input_ports attribute and num_feature_input_ports property • parameterestimationcomposition.py: fixed misplacement of its Parameters() attribute • optimizationfunctions.py: made num_estimates a Parameter * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * - * - * • optimizationcontrolmechanism.py: - add state_feature_spec parameter that is assigned value of state_features arg specified in constructor * • optimizationcontrolmechanism.py: - replace self._state_feature_specs_parsed with assignments to self.state_feature_specs Parameter (using .spec for user-assigned values): Problems: 1) .spec is a copy, so that evaluation of specified objects fails (e.g., determining whether a specified Mechanism is in the list of INPUT node of agent_rep returned by _get_agent_rep_input_nodes(), as in _validate_input_nodes()) 2) fails in LLVM, even if state_parameter_specs Parameter is included in blacklist in Component._get_compilation_params * • optimizationcontrolmechanism.py: - both problems fixed * - Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- psyneulink/core/components/component.py | 2 +- .../control/optimizationcontrolmechanism.py | 124 +++++++++--------- psyneulink/core/globals/utilities.py | 2 +- tests/composition/test_composition.py | 4 +- tests/composition/test_control.py | 4 +- 5 files changed, 65 insertions(+), 71 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index c3ae9d3d1f9..47812995aca 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1382,7 +1382,7 @@ def _get_compilation_params(self): "monitor_for_control", "state_feature_values", "simulation_ids", "input_labels_dict", "output_labels_dict", "num_estimates", "modulated_mechanisms", "grid", "control_signal_params", - "activation_derivative_fct", "input_specification", + "activation_derivative_fct", "input_specification", "state_feature_specs", # Reference to other components "objective_mechanism", "agent_rep", "projections", # Shape mismatch diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 929c998559a..4efa968ecbe 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1040,7 +1040,7 @@ def _state_feature_values_getter(owning_component=None, context=None): j = 0 state_feature_values = [] for node, spec in zip(owning_component._specified_input_nodes_in_order, - owning_component._state_feature_specs_parsed): + owning_component.state_feature_specs): if spec is not None: state_feature_values.append(state_input_port_values[j]) j += 1 @@ -1403,7 +1403,6 @@ class OptimizationControlMechanism(ControlMechanism): # PREFERENCE_SET_NAME: 'DefaultControlMechanismCustomClassPreferences', # PREFERENCE_KEYWORD: ...} - # FIX: ADD OTHER Parameters() HERE?? class Parameters(ControlMechanism.Parameters): """ Attributes @@ -1508,7 +1507,8 @@ class Parameters(ControlMechanism.Parameters): """ outcome_input_ports_option = Parameter(CONCATENATE, stateful=False, loggable=False, structural=True) state_input_ports = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) - # state_feature_specs = Parameter(None, stateful=False, loggable=False, read_only=True, structural=True) + state_feature_specs = Parameter(None, stateful=False, loggable=False, read_only=True, + structural=True, parse_spec=True) state_feature_function = Parameter(None, reference=True, stateful=False, loggable=False) function = Parameter(GridSearch, stateful=False, loggable=False) search_function = Parameter(None, stateful=False, loggable=False) @@ -1539,17 +1539,22 @@ class Parameters(ControlMechanism.Parameters): saved_samples = None saved_values = None - # # MODIFIED 1/30/22 NEW: FIX - MAY BE NEEDED IF state_feature_specs -> Parameter, - # WHICH SHOULD SET spec ATTRIBUTE - # def _parse_state_feature_specs(self, state_features): - # return (state_features if isinstance(state_features, (dict, set)) else convert_to_list(state_features)) - # MODIFIED 1/30/22 END + def _parse_state_feature_specs(self, state_features): + # return (state_features if isinstance(state_features, (dict, set)) else convert_to_list(state_features)) + from psyneulink.core.compositions.composition import Composition + return (state_features if (isinstance(state_features, set) + or (isinstance(state_features, dict) + and (any(isinstance(key, (Port, Mechanism, Composition)) + for key in state_features) + or SHADOW_INPUTS in state_features))) + else convert_to_list(state_features)) @handle_external_context() @tc.typecheck def __init__(self, agent_rep=None, - state_features: tc.optional(tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort))) = None, + state_features: tc.optional(tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort))) = + None, state_feature_function: tc.optional(tc.optional(tc.any(dict, is_function_type))) = None, function=None, num_estimates = None, @@ -1592,20 +1597,6 @@ def __init__(self, kwargs.pop('feature_function') continue - # FIX: 1/30/22 - REMOVE IF state_feature_specs -> Parameter AND SET IN Parameter._parse_state_feature_specs - # # MODIFIED 1/30/22 OLD: - # self.state_feature_specs = (state_features if isinstance(state_features, (dict, set)) - # else convert_to_list(state_features)) - # MODIFIED 1/30/22 NEW: - # Enclose state_features in a list unless it is already a list, set, or state_feature specification dict - self.state_feature_specs = (state_features if (isinstance(state_features, set) - or (isinstance(state_features, dict) - and (any(isinstance(key, (Port, Mechanism, Composition)) - for key in state_features) - or SHADOW_INPUTS in state_features))) - else convert_to_list(state_features)) - # MODIFIED 1/30/22 END - function = function or GridSearch # If agent_rep hasn't been specified, put into deferred init @@ -1615,6 +1606,7 @@ def __init__(self, self._assign_deferred_init_name(self.__class__.__name__) # Store args for deferred initialization self._store_deferred_init_args(**locals()) + self._init_args['state_feature_specs'] = state_features # Flag for deferred initialization self.initialization_status = ContextFlags.DEFERRED_INIT @@ -1633,6 +1625,7 @@ def __init__(self, self._assign_deferred_init_name(self.__class__.__name__) # Store args for deferred initialization self._store_deferred_init_args(**locals()) + self._init_args['state_feature_specs'] = state_features # Flag for deferred initialization self.initialization_status = ContextFlags.DEFERRED_INIT @@ -1640,9 +1633,7 @@ def __init__(self, super().__init__( agent_rep=agent_rep, - # # MODIFIED 1/30/22 NEW: FIX - MAY NEED IF state_feature_specs -> Parameter - # state_feature_specs=state_features, - # MODIFIED 1/30/22 END + state_feature_specs=state_features, state_feature_function=state_feature_function, function=function, num_estimates=num_estimates, @@ -1885,27 +1876,23 @@ def _parse_state_feature_specs(self, context=None): # The following are all "full" lists; that is, there is an entry corresponding to every INPUT node of agent_rep # List of INPUT Nodes for which state_features are specified, ordered according to agent_rep.nodes self._specified_input_nodes_in_order = [] - # List of parsed state_feature_specs (vs. user provided specs) - self._state_feature_specs_parsed = [] # List of assigned state_feature_function (vs. user provided specs) self._state_feature_functions = [] # VALIDATION AND WARNINGS ----------------------------------------------------------------------------------- - state_feature_specs = self.state_feature_specs - # Only list spec allowed if agent_rep is a CompositionFunctionApproximator - if self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR and not isinstance(state_feature_specs, list): + if self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR and not isinstance(self.state_feature_specs, list): agent_rep_name = f" ({self.agent_rep.name})" if not isinstance(self.agent_rep, type) else '' raise OptimizationControlMechanismError( f"The {AGENT_REP} specified for {self.name}{agent_rep_name} is a {COMPOSITION_FUNCTION_APPROXIMATOR}, " - f"so its '{STATE_FEATURES}' argument must be a list, not a {type(state_feature_specs).__name__} " - f"({state_feature_specs}).") + f"so its '{STATE_FEATURES}' argument must be a list, not a {type(self.state_feature_specs).__name__} " + f"({self.state_feature_specs}).") # agent_rep has not yet been (fully) constructed if not agent_rep_input_nodes and self.agent_rep_type is COMPOSITION: - if (isinstance(state_feature_specs, set) - or isinstance(state_feature_specs, dict) and SHADOW_INPUTS not in state_feature_specs): + if (isinstance(self.state_feature_specs, set) + or isinstance(self.state_feature_specs, dict) and SHADOW_INPUTS not in self.state_feature_specs): # Dict and set specs reference Nodes of agent_rep, and so must that must be constructed first raise OptimizationControlMechanismError( f"The '{STATE_FEATURES}' arg for {self.name} has been assigned a dict or set specification " @@ -1922,7 +1909,7 @@ def _parse_state_feature_specs(self, context=None): else: # # FIX: 1/16/22 - MAY BE A PROBLEM IF SET OR DICT HAS ENTRIES FOR INPUT NODES OF NESTED COMP THAT IS AN INPUT NODE # FIX: 1/18/22 - ADD TEST FOR THIS WARNING TO test_ocm_state_feature_specs_and_warnings_and_errors: too_many_inputs - if len(state_feature_specs) < len(agent_rep_input_nodes): + if len(self.state_feature_specs) < len(agent_rep_input_nodes): warnings.warn(f"There are fewer '{STATE_FEATURES}' specified for '{self.name}' than the number of " f"INPUT Nodes of its {AGENT_REP} ('{self.agent_rep.name}'); the remaining inputs will be " f"assigned default values when '{self.agent_rep.name}`s 'evaluate' method is executed. " @@ -1954,6 +1941,8 @@ def _parse_specs(state_feature_specs, spec_str="list"): Return names for use as input_port_names in main body of method """ + parsed_feature_specs = [] + if self.agent_rep_type == COMPOSITION: if len(state_feature_specs) > len(agent_rep_input_nodes): nodes_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" @@ -1962,7 +1951,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): node_str = ", ".join(nodes_not_in_agent_rep) warnings.warn( f"The number of '{STATE_FEATURES}' specified for {self.name} " - f"({len(self.state_feature_specs)}) is more than the number of INPUT Nodes " + f"({len(state_feature_specs)}) is more than the number of INPUT Nodes " f"({len(agent_rep_input_nodes)}) of the Composition assigned as its {AGENT_REP} " f"('{self.agent_rep.name}'), which includes the following that " f"are not in '{self.agent_rep.name}': {node_str}. Executing {self.name} before the " @@ -1970,7 +1959,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): else: warnings.warn( f"The number of '{STATE_FEATURES}' specified for {self.name} " - f"({len(self.state_feature_specs)}) is more than the number of INPUT Nodes " + f"({len(state_feature_specs)}) is more than the number of INPUT Nodes " f"({len(agent_rep_input_nodes)}) of the Composition assigned as its {AGENT_REP} " f"('{self.agent_rep.name}'). Executing {self.name} before the " f"additional Nodes are added as INPUT Nodes will generate an error.") @@ -2037,51 +2026,54 @@ def _parse_specs(state_feature_specs, spec_str="list"): # the remaining ones may be specified later, but for now assume they are meant to be ignored spec = None - self._state_feature_specs_parsed.append(spec) + parsed_feature_specs.append(spec) self._state_feature_functions.append(state_feature_fct) self._specified_input_nodes_in_order.append(node) spec_names.append(spec_name) + self.parameters.state_feature_specs.set(parsed_feature_specs, override=True) return spec_names or [] + user_specs = self.parameters.state_feature_specs.spec + # user_specs = self.state_feature_specs + # LIST spec # Treat as source specs: # - construct a regular dict using INPUT Nodes as keys and specs as values - if isinstance(state_feature_specs, list): - input_port_names = _parse_specs(state_feature_specs) + if isinstance(user_specs, list): + input_port_names = _parse_specs(user_specs) # DICT spec - elif isinstance(state_feature_specs, dict): + elif isinstance(user_specs, dict): # SHADOW_INPUTS dict spec - if SHADOW_INPUTS in state_feature_specs: + if SHADOW_INPUTS in user_specs: # Set not allowed as SHADOW_INPUTS spec - if isinstance(state_feature_specs[SHADOW_INPUTS], set): + if isinstance(user_specs[SHADOW_INPUTS], set): # Catch here to provide context-relevant error message raise OptimizationControlMechanismError( f"The '{STATE_FEATURES}' argument for '{self.name}' uses a set in a '{SHADOW_INPUTS.upper()}' " f"dict; this must be a single item or list of specifications in the order of the INPUT Nodes" f"of its '{AGENT_REP}' ({self.agent_rep.name}) to which they correspond." ) - input_port_names = _parse_specs(state_feature_specs[SHADOW_INPUTS], - f"{SHADOW_INPUTS.upper()} dict") + input_port_names = _parse_specs(user_specs[SHADOW_INPUTS], f"{SHADOW_INPUTS.upper()} dict") # User {node:spec} dict spec else: - specified_input_nodes = state_feature_specs.keys() + specified_input_nodes = user_specs.keys() self._validate_input_nodes(specified_input_nodes) nodes = self._get_agent_rep_input_nodes(comp_as_node=True) # Get specs in order of agent_rep INPUT Nodes, with None assigned to any unspecified INPUT Nodes # as well as to any not in agent_rep at end which are placed at the end of the list nodes.extend([node for node in specified_input_nodes if node not in nodes]) - specs = [state_feature_specs[node] if node in specified_input_nodes else None for node in nodes] + specs = [user_specs[node] if node in specified_input_nodes else None for node in nodes] # Get parsed specs and names (don't care about nodes since those are specified by keys input_port_names = _parse_specs(specs) # SET spec # Treat as specification of INPUT Nodes to be shadowed: # - construct an InputPort dict with SHADOW_INPUTS as its key, and specs in a list as its value - elif isinstance(state_feature_specs, set): + elif isinstance(user_specs, set): # All nodes must be INPUT nodes of agent_rep, that are to be shadowed, - self._validate_input_nodes(state_feature_specs) + self._validate_input_nodes(user_specs) # specs = [node if node in state_feature_specs else None for node in agent_rep_input_nodes] # FIX: 1/29/22 - # THIS IS DANGEROUS -- SHOULD REPLACE ONCE TUPLE FORMAT IS IMPLEMENTED OR USE InputPort SPECIF DICT @@ -2091,7 +2083,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): # ONCE FIXED, EXTEND FOR USE WITH COMP AS SPEC IN LIST AND DICT FORMATS # Replace any nested Comps that are INPUT Nodes of agent_comp with their INPUT Nodes so they are shadowed all_nested_input_nodes = [] - for node in state_feature_specs: + for node in user_specs: if isinstance(node, Composition): all_nested_input_nodes.extend(get_inputs_for_nested_comp(node)) else: @@ -2106,8 +2098,8 @@ def _parse_specs(state_feature_specs, spec_str="list"): state_input_port_specs = [] for i in range(self._num_state_feature_specs): - spec = self._state_feature_specs_parsed[i] - # node = self._specified_input_nodes_in_order[i] + # Note: state_feature_specs have now been parsed (user's specs are in parameters.state_feature_specs.spec) + spec = self.state_feature_specs[i] if spec is None: continue spec = _parse_shadow_inputs(self, spec) @@ -2133,7 +2125,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): else: spec = spec.output_port # Update Mechanism spec with Port - self._state_feature_specs_parsed[i] = spec + self.state_feature_specs[i] = spec if isinstance(spec, dict): # Note: clear any functions specified; will be assigned in _assign_state_feature_function if self._state_feature_functions[i]: @@ -2202,13 +2194,13 @@ def _update_state_features_dict(self): # FIX: HANDLE ERRORS HERE INSTEAD OF _validate_state_features OR EXECUTE THAT FIRST AND CAPTURE HERE for i, port in enumerate(self.state_input_ports): node = self._specified_input_nodes_in_order[i] - feature = self._state_feature_specs_parsed[i] + feature = self.state_feature_specs[i] if not (isinstance(node, str) and 'DEFERRED' in node): continue if feature.owner not in self._get_agent_rep_input_nodes(): # assert False, f"PROGRAM ERROR: Node not in {self.agent_rep.name} should have been caught above." continue - self._state_feature_specs_parsed[i] = feature + self.state_feature_specs[i] = feature def _update_state_input_ports_for_controller(self, context=None): """Check and update state_input_ports for model-based optimization (agent_rep==Composition) @@ -2303,10 +2295,12 @@ def _update_state_input_ports_for_controller(self, context=None): # Assign OptimizationControlMechanism attributes self.state_input_ports.data = state_input_ports self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) - self._state_feature_specs_parsed = [input_port.shadow_inputs for input_port in self.state_input_ports] - + self.parameters.state_feature_specs.set([input_port.shadow_inputs + for input_port in self.state_input_ports], + override=True) self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) - self._state_feature_specs_parsed = [input_port.shadow_inputs for input_port in self.state_input_ports] + self.parameters.state_feature_specs.set([input_port.shadow_inputs for input_port in self.state_input_ports], + override=True) return True def _validate_state_features(self): @@ -2328,11 +2322,13 @@ def _validate_state_features(self): Composition, CompositionInterfaceMechanism, CompositionError, RunError, NodeRole comp = self.agent_rep + user_specs = self.parameters.state_feature_specs.spec + # user_specs = self.state_feature_specs - if isinstance(self.state_feature_specs, dict) and SHADOW_INPUTS in self.state_feature_specs: - state_feature_specs = self.state_feature_specs[SHADOW_INPUTS] + if isinstance(user_specs, dict) and SHADOW_INPUTS in user_specs: + state_feature_specs = user_specs[SHADOW_INPUTS] else: - state_feature_specs = self.state_feature_specs + state_feature_specs = user_specs if isinstance(state_feature_specs, list): # Convert list to dict, assuming list is in order of INPUT Nodes, @@ -3109,11 +3105,9 @@ def num_state_input_ports(self): @property def state_features(self): - # FIX: 1/30/22 - REFACTOR TO USE _state_feature_specs_parsed and _specified_input_nodes_in_order agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) state_features_dict = {(k if k in agent_rep_input_nodes else f"{k.name} DEFERRED"):v - for k,v in zip(self._specified_input_nodes_in_order, - self._state_feature_specs_parsed)} + for k,v in zip(self._specified_input_nodes_in_order, self.state_feature_specs)} return state_features_dict @property diff --git a/psyneulink/core/globals/utilities.py b/psyneulink/core/globals/utilities.py index 73b8a682db3..98212b36c17 100644 --- a/psyneulink/core/globals/utilities.py +++ b/psyneulink/core/globals/utilities.py @@ -801,7 +801,7 @@ def copy_iterable_with_shared(obj, shared_types=None, memo=None): dict_types = (dict, collections.UserDict) list_types = (list, collections.UserList, collections.deque) - tuple_types = (tuple, ) + tuple_types = (tuple, set) all_types_using_recursion = dict_types + list_types + tuple_types if isinstance(obj, dict_types): diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 6d7d18a3056..c8129f2d425 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -381,11 +381,11 @@ def test_add_multiple_projections_for_nested_compositions(self, projs, expected_ X = ProcessingMechanism(name='INPUT NODE') M = ProcessingMechanism(name='MIDDLE NODE') Y = ProcessingMechanism(name='OUTPUT NODE') - if projs is 'list': + if projs == 'list': iprojs = [MappingProjection(sender=C, receiver=D, matrix=RANDOM_CONNECTIVITY_MATRIX)] oprojs = [MappingProjection(sender=X, receiver=A, matrix=RANDOM_CONNECTIVITY_MATRIX), MappingProjection(sender=X, receiver=M, matrix=RANDOM_CONNECTIVITY_MATRIX)] - elif projs is 'set': + elif projs == 'set': iprojs = {MappingProjection(sender=C, receiver=D, matrix=RANDOM_CONNECTIVITY_MATRIX)} oprojs = {MappingProjection(sender=X, receiver=A, matrix=RANDOM_CONNECTIVITY_MATRIX), MappingProjection(sender=X, receiver=M, matrix=RANDOM_CONNECTIVITY_MATRIX)} diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 22b1dd85161..59a7fd181b2 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -922,8 +922,8 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'too_many_w_node_not_in_composition_warning': [ia, oa, ob, ext], 'bad_dict_spec_warning': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node 'bad_dict_spec_error': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node - 'bad_set_spec_warning': {ob, ia}, # elicits both short spec and not INPUT Node warnings (for both ob and ia) - 'bad_set_spec_error': {ob, ia}, # elicits both short spec and not INPUT Node warnings (for both ob and ia) + 'bad_set_spec_warning': {ob, ia}, # elicits short spec warning + 'bad_set_spec_error': {ob, ia}, # elicits INPUT Node warning (for ia) 'comp_in_list_spec':[icomp, oa.output_port, [3,1,2]], # FIX: REMOVE ONCE TUPLE FORMAT SUPPORTED 'comp_in_shadow_inupts_spec':{pnl.SHADOW_INPUTS:[icomp, oa, ob]} } From d7f68ad4868f6925ab32ecbf211adad890fa3fbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Feb 2022 02:33:01 +0000 Subject: [PATCH 152/285] requirements: update elfi requirement from <0.8.3 to <0.8.4 (#2325) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0ab1e961c16..816b11ee73c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ autograd<=1.3 graph-scheduler>=0.2.0, <1.0.1 dill<=0.32 -elfi<0.8.3 +elfi<0.8.4 graphviz<0.20.0 grpcio<1.43.0 grpcio-tools<1.43.0 From 4b5818f70ede52bbdc3a9eb810ba4fb73e89f337 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Fri, 18 Feb 2022 10:45:40 -0500 Subject: [PATCH 153/285] Refactor/ports/restrict projections (#2327) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - modified test_mode_based_num_estimates * - * - * • optimizationcontrolmechanism.py: - _instantiate_control_signals: random_seeds -> random_seed_mod_values * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: now adds feature_input_ports for Compostion INPUT nodes if not state_features not specified * - * • composition.py - _add_controller: modifying to instantiate feature_input_ports if none are specified * • composition.py: - add_controller: assign simulation_input_ports * - * • optimizationcontrolmechanism.py: - feature_input_ports -> state_input_ports - _instantiate_input_ports(): state_features only allowed to specifying state_input_ports if agent_rep is a CompositionFunctionApproximator (i.e., model-free optimization) • composition.py: - add_controller: adds state_input_ports to shadow INPUT Nodes of Composition if controller.agent_rep is Composition (model-based optimziation) or state_features have not been specified (for model-free optimizaton) * - * • optimizationcontrolmechanism.py: _instantiate_input_ports: reinstate allowance of state_features specification if agent_rep is a Composition (i.e., model-based optimization) as long as they are all INPUT Nodes of agent_rep * - * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_estimates_per_trial * - * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trial_per_estimate * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * - * • port.py: - refactor to eliminate: - efferents attribute from InputPorts and Parameters - path_afferents attribute from OutputPports - add remove_projections() - add mod_afferents property - _get_input_struct_type(): add try and accept for path_afferents • inputport.py: - add path_afferents override • parameterport.py: - add path_afferents override • outputport.py: - add efferents override • composition.py: - add_projection(): call port.remove_projection • keywords.py: add PATH_AFFERENTS, MOD_AFFERENTS, EFFERENTS * - * • Passes all tests * - * • test_input_ports.py: - add test_no_efferents() * • test_output_ports.py: - add test_no_path_afferents() * • test_parameter_ports.py: - add test_no_path_afferents() - add test_no_efferents() * - Co-authored-by: jdcpni Co-authored-by: Jan Vesely Co-authored-by: Katherine Mantel --- psyneulink/core/components/ports/inputport.py | 27 +++-- .../core/components/ports/outputport.py | 15 +++ .../core/components/ports/parameterport.py | 7 +- psyneulink/core/components/ports/port.py | 114 +++++++++++++----- psyneulink/core/compositions/composition.py | 33 ++--- psyneulink/core/globals/keywords.py | 12 +- tests/composition/test_composition.py | 16 ++- tests/ports/test_input_ports.py | 11 ++ tests/ports/test_output_ports.py | 11 ++ tests/ports/test_parameter_ports.py | 23 ++++ 10 files changed, 202 insertions(+), 67 deletions(-) diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 3655423c0a2..2f8415c32eb 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -521,13 +521,14 @@ """ import collections import inspect +import itertools import numbers import warnings import numpy as np import typecheck as tc -from psyneulink.core.components.component import DefaultsFlexibility +from psyneulink.core.components.component import Component, DefaultsFlexibility from psyneulink.core.components.functions.function import Function from psyneulink.core.components.functions.nonstateful.combinationfunctions import CombinationFunction, LinearCombination from psyneulink.core.components.ports.outputport import OutputPort @@ -1001,14 +1002,13 @@ def _check_for_duplicate_projections(self, projection): Returns redundant Projection if found, otherwise False. """ + if self.initialization_status == ContextFlags.DEFERRED_INIT: + raise InputPortError(f"Attempt to assign Projection ('{projection.name}') " + f"using InputPort ('{self.name}') that is in deferred init") try: self.path_afferents except: - if self.initialization_status == ContextFlags.DEFERRED_INIT: - raise InputPortError(f"Attempt to assign Projection ('{projection}') " - f"to InputPort ('{self.name}') that is in deferred init") - else: - raise InputPortError(f"No 'path_afferents' for {self.name}") + raise InputPortError(f"No 'path_afferents' for {self.full_name}") # FIX: 7/22/19 - CHECK IF SENDER IS SPECIFIED AS MECHANISM AND, IF SO, CHECK ITS PRIMARY_OUTPUT_PORT duplicate = next(iter([proj for proj in self.path_afferents @@ -1053,6 +1053,12 @@ def _get_variable_from_projections(self, context=None): def _get_primary_port(self, mechanism): return mechanism.input_port + def _get_all_afferents(self): + return self.path_afferents + self.mod_afferents + + def _get_all_projections(self): + return self._get_all_afferents() + @tc.typecheck def _parse_port_specific_specs(self, owner, port_dict, port_specific_spec): """Get weights, exponents and/or any connections specified in an InputPort specification tuple @@ -1328,6 +1334,14 @@ def pathway_projections(self): def pathway_projections(self, assignment): self.path_afferents = assignment + @property + def path_afferents(self): + try: + return self._path_afferents + except: + self._path_afferents = [] + return self._path_afferents + @property def socket_width(self): return self.defaults.variable.shape[-1] @@ -1407,7 +1421,6 @@ def _get_port_function_value(owner, function, variable): return Port_Base._get_port_function_value(owner=owner, function=function, variable=variable) - def _instantiate_input_ports(owner, input_ports=None, reference_value=None, context=None): """Call Port._instantiate_port_list() to instantiate ContentAddressableList of InputPort(s) diff --git a/psyneulink/core/components/ports/outputport.py b/psyneulink/core/components/ports/outputport.py index ef2213aafc2..6a6cb5f5056 100644 --- a/psyneulink/core/components/ports/outputport.py +++ b/psyneulink/core/components/ports/outputport.py @@ -764,6 +764,7 @@ def __init__(self, error_value): def __str__(self): return repr(self.error_value) + class OutputPort(Port_Base): """ OutputPort( \ @@ -1056,6 +1057,12 @@ def _check_for_duplicate_projections(self, projection): def _get_primary_port(self, mechanism): return mechanism.output_port + def _get_all_afferents(self): + return self.mod_afferents + + def _get_all_projections(self): + return self.mod_afferents + self.efferents + def _parse_arg_variable(self, default_variable): return _parse_output_port_variable(default_variable, self.owner) @@ -1263,6 +1270,14 @@ def pathway_projections(self): def pathway_projections(self, assignment): self.efferents = assignment + @property + def efferents(self): + try: + return self._efferents + except: + self._efferents = [] + return self._efferents + # For backward compatibility with INDEX and ASSIGN @property def calculate(self): diff --git a/psyneulink/core/components/ports/parameterport.py b/psyneulink/core/components/ports/parameterport.py index 69431683dba..e0d882eda06 100644 --- a/psyneulink/core/components/ports/parameterport.py +++ b/psyneulink/core/components/ports/parameterport.py @@ -796,6 +796,12 @@ def _check_for_duplicate_projections(self, projection): f'{self.owner.name} already exists; will ignore additional one specified ({projection.name}).') return duplicate + def _get_all_afferents(self): + return self.mod_afferents + + def _get_all_projections(self): + return self.mod_afferents + @tc.typecheck def _parse_port_specific_specs(self, owner, port_dict, port_specific_spec): """Get connections specified in a ParameterPort specification tuple @@ -962,7 +968,6 @@ def _get_variable_from_projections(self, context=None): """ Get backingfield ("base") value of param of function of Mechanism to which the ParameterPort belongs. """ - # FIX 3/6/19: source does not yet seem to have been assigned to owner.function return self.source._get(context) diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index e3b125df8a2..d49ff82ca07 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -793,7 +793,7 @@ def test_multiple_modulatory_projections_with_mech_and_port_Name_specs(self): MATRIX, MECHANISM, MODULATORY_PROJECTION, MODULATORY_PROJECTIONS, MODULATORY_SIGNAL, \ MULTIPLICATIVE, MULTIPLICATIVE_PARAM, \ NAME, OUTPUT_PORTS, OVERRIDE, OWNER, \ - PARAMETER_PORTS, PARAMS, PATHWAY_PROJECTIONS, PREFS_ARG, \ + PATH_AFFERENTS, PARAMETER_PORTS, PARAMS, PATHWAY_PROJECTIONS, PREFS_ARG, \ PROJECTION_DIRECTION, PROJECTIONS, PROJECTION_PARAMS, PROJECTION_TYPE, \ RECEIVER, REFERENCE_VALUE, REFERENCE_VALUE_NAME, SENDER, STANDARD_OUTPUT_PORTS, \ PORT, PORT_COMPONENT_CATEGORY, PORT_CONTEXT, Port_Name, port_params, PORT_PREFS, PORT_TYPE, port_value, \ @@ -1080,7 +1080,6 @@ def __init__(self, if name is not None and DEFERRED_INITIALIZATION in name: name = self._assign_default_port_Name() - # Register Port with PortRegistry of owner (Mechanism to which the Port is being assigned) register_category(entry=self, base_class=Port_Base, @@ -1101,9 +1100,6 @@ def __init__(self, **kwargs ) - self.path_afferents = [] - self.mod_afferents = [] - # IMPLEMENTATION NOTE: MOVE TO COMPOSITION ONCE THAT IS IMPLEMENTED # INSTANTIATE PROJECTIONS SPECIFIED IN projections ARG OR params[PROJECTIONS:<>] if self.projections is not None: @@ -1114,7 +1110,7 @@ def __init__(self, # if params = NotImplemented or there is no param[PROJECTIONS] pass - self.projections = self.path_afferents + self.mod_afferents + self.efferents + self.projections = self._get_all_projections() if context.source == ContextFlags.COMMAND_LINE: owner.add_ports([self]) @@ -1796,6 +1792,22 @@ def _get_receiver_port(spec): self.owner.aux_components.append((projection, feedback)) return projection + def remove_projection(self, projection, context=None): + if projection in self.afferents_info: + del self.afferents_info[projection] + if projection in self.projections: + self.projections.remove(projection) + try: + if projection in self.mod_afferents or projection in self.path_afferents: + self._remove_projection_to_port(projection, context=context) + except(PortError): + pass + try: + if projection in self.efferents: + self._remove_projection_from_port(projection, context=context) + except(PortError): + pass + def _remove_projection_from_port(self, projection, context=None): """Remove Projection entry from Port.efferents.""" del self.efferents[self.efferents.index(projection)] @@ -1808,6 +1820,9 @@ def _remove_projection_to_port(self, projection, context=None): if projection in self.mod_afferents: del self.mod_afferents[self.mod_afferents.index(projection)] else: + # Do this first so that if it fails (i.e., miscalled for OutputPort) + # no changes are made to the Port's or its function's variable + del self.path_afferents[self.path_afferents.index(projection)] shape = list(self.defaults.variable.shape) # Reduce outer dimension by one # only if shape is already greater than 1 (ports keep @@ -1816,12 +1831,17 @@ def _remove_projection_to_port(self, projection, context=None): if shape[0] > 0: self.defaults.variable = np.resize(self.defaults.variable, shape) self.function.defaults.variable = np.resize(self.function.defaults.variable, shape) - del self.path_afferents[self.path_afferents.index(projection)] def _get_primary_port(self, mechanism): raise PortError("PROGRAM ERROR: {} does not implement _get_primary_port method". format(self.__class__.__name__)) + def _get_all_projections(self): + assert False, f"Subclass of Port ({self.__class__.__name__}) must implement '_get_all_projections()' method." + + def _get_all_afferents(self): + assert False, f"Subclass of Port ({self.__class__.__name__}) must implement '_get_all_afferents()' method." + def _parse_port_specific_specs(self, owner, port_dict, port_specific_spec): """Parse parameters in Port specification tuple specific to each subclass @@ -2225,7 +2245,7 @@ def owner(self, assignment): @property def all_afferents(self): - return self.path_afferents + self.mod_afferents + return self._get_all_afferents() @property def afferents_info(self): @@ -2235,22 +2255,45 @@ def afferents_info(self): self._afferents_info = {} return self._afferents_info + # IMPLEMENTATION NOTE: + # Every Port subtype has mod_afferents + # path_afferents are specific to InputPorts + # efferents are specific to OutputPorts + @property - def efferents(self): + def mod_afferents(self): try: - return self._efferents + return self._mod_afferents except: - self._efferents = [] - return self._efferents + self._mod_afferents = [] + return self._mod_afferents + + @property + def path_afferents(self): + raise PortError(f"{self.__class__.__name__}s do not have 'path_afferents'; " + f"(access attempted for {self.full_name}).") + + @path_afferents.setter + def path_afferents(self, value): + raise PortError(f"{self.__class__.__name__}s are not allowed to have 'path_afferents' " + f"(assignment attempted for {self.full_name}).") + + @property + def efferents(self): + # assert False, f"{self.__class__.__name__} must implement 'efferents' property." + raise PortError(f"{self.__class__.__name__}s do not have 'efferents'; " + f"(access attempted for {self.full_name}).") @efferents.setter def efferents(self, proj): - assert False, f"Illegal attempt to directly assign {repr('efferents')} attribute of {self.name}" + # assert False, f"Illegal attempt to directly assign {repr('efferents')} attribute of {self.name}" + raise PortError(f"{self.__class__.__name__}s are not allowed to have 'efferents' " + f"(assignment attempted for {self.full_name}).") @property def full_name(self): """Return name relative to owner as: []""" - if self.owner: + if hasattr(self, OWNER) and self.owner: return f'{self.owner.name}[{self.name}]' else: return self.name @@ -2270,13 +2313,16 @@ def _get_input_struct_type(self, ctx): # Use function input type. The shape should be the same, # however, some functions still need input shape workarounds. func_input_type = ctx.get_input_struct_type(self.function) - # MODIFIED 4/4/20 NEW: [PER JAN] - if len(self.path_afferents) > 0: - assert len(func_input_type) == len(self.path_afferents), \ - "{} shape mismatch: {}\nport:\n\t{}\n\tfunc: {}\npath_afferents: {}".format( - self, func_input_type, self.defaults.variable, - self.function.defaults.variable, len(self.path_afferents)) - # MODIFIED 4/4/20 END + try: + # MODIFIED 4/4/20 NEW: [PER JAN] + if len(self.path_afferents) > 0: + assert len(func_input_type) == len(self.path_afferents), \ + "{} shape mismatch: {}\nport:\n\t{}\n\tfunc: {}\npath_afferents: {}".format( + self, func_input_type, self.defaults.variable, + self.function.defaults.variable, len(self.path_afferents)) + # MODIFIED 4/4/20 END + except (PortError): + pass input_types = [func_input_type] # Add modulation for mod in self.mod_afferents: @@ -2370,10 +2416,16 @@ def _get_port_function_value(owner, function, variable, context=None): @property def _dependent_components(self): - return list(itertools.chain( - super()._dependent_components, - self.efferents, - )) + try: + return list(itertools.chain( + super()._dependent_components, + self.efferents, + )) + except PortError: + return list(itertools.chain( + super()._dependent_components, + )) + @property def _dict_summary(self): @@ -2504,9 +2556,15 @@ def _instantiate_port_list(owner, port_spec=port_spec, # name=name, context=context) - # automatically generate projections (e.g. when an InputPort is specified by the OutputPort of another mech) - for proj in port.path_afferents: - owner.aux_components.append(proj) + # automatically generate any Projections to InputPort or ParameterPort + # (e.g. if InputPort was specified using the OutputPort of another Mechanism, + # or a ParameterPort was specified using the ControlSignal of a ControlMechanism) + try: + for proj in port.path_afferents: + owner.aux_components.append(proj) + except PortError: + # OutputPort that has no path_afferents + pass # KDM 12/3/19: this depends on name setting for InputPorts that # ensures there are no duplicates. If duplicates exist, ports diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 3905bd62f19..504925bac31 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2624,7 +2624,7 @@ def input_function(env, result): from psyneulink.core.components.ports.modulatorysignals.controlsignal import ControlSignal from psyneulink.core.components.ports.outputport import OutputPort from psyneulink.core.components.ports.parameterport import ParameterPort -from psyneulink.core.components.ports.port import Port +from psyneulink.core.components.ports.port import Port, PortError from psyneulink.core.components.projections.modulatory.controlprojection import ControlProjection from psyneulink.core.components.projections.modulatory.learningprojection import LearningProjection from psyneulink.core.components.projections.modulatory.modulatoryprojection import ModulatoryProjection_Base @@ -5403,26 +5403,17 @@ def add_projection(self, else: receiver_check = receiver # If either the sender or receiver are not in Composition and are not CompositionInterfaceMechanisms - # remove the Projection and inclusion in relevant Ports + # remove the Projection and its inclusion in any relevant Port attributes if ((not isinstance(sender_check, CompositionInterfaceMechanism) and sender_check not in self.nodes) or (not isinstance(receiver_check, CompositionInterfaceMechanism) and receiver_check not in self.nodes)): for proj in existing_projections: self.remove_projection(proj) - for port in receiver_check.input_ports + sender_check.output_ports: - if proj in port.afferents_info: - del port.afferents_info[proj] - if proj in port.projections: - port.projections.remove(proj) - if proj in port.path_afferents: - port.path_afferents.remove(proj) - if proj in port.mod_afferents: - port.mod_afferents.remove(proj) - if proj in port.efferents: - port.efferents.remove(proj) + for port in sender_check.output_ports + receiver_check.input_ports: + port.remove_projection(proj, context=context) else: - # Need to do stuff at end, so can't just return + # Need to do stuff at end, so can't just return if self.prefs.verbosePref: warnings.warn(f"Several existing projections were identified between " f"{sender.name} and {receiver.name}: {[p.name for p in existing_projections]}; " @@ -5588,15 +5579,15 @@ def _validate_projection(self, learning_projection, ): - # FIX: [JDC 6/8/19] SHOULDN'T THERE BE A CHECK FOR THEM LearningProjections? OR ARE THOSE DONE ELSEWHERE? + # FIX: [JDC 6/8/19] SHOULDN'T THERE BE A CHECK FOR THEM IN LearningProjections? OR ARE THOSE DONE ELSEWHERE? # Skip this validation on learning projections because they have non-standard senders and receivers if not learning_projection: if projection.sender.owner != graph_sender: - raise CompositionError("{}'s sender assignment [{}] is incompatible with the positions of these " - "Components in the Composition.".format(projection, sender)) + raise CompositionError(f"Sender ('{sender.name}') assigned to '{projection.name} is " + f"incompatible with the positions of these Components in '{self.name}'.") if projection.receiver.owner != graph_receiver: - raise CompositionError("{}'s receiver assignment [{}] is incompatible with the positions of these " - "Components in the Composition.".format(projection, receiver)) + raise CompositionError(f"Receiver ('{receiver.name}') assigned to '{projection.name} is " + f"incompatible with the positions of these Components in '{self.name}'.") def _instantiate_projection_from_spec(self, projection, sender=None, receiver=None, name=None): if isinstance(projection, dict): @@ -7960,11 +7951,11 @@ def _route_control_projection_through_intermediary_pcims(self, graph_receiver.add_projection(p, receiver=p.receiver, sender=control_signal) try: sender._remove_projection_to_port(projection) - except ValueError: + except (ValueError, PortError): pass try: receiver._remove_projection_from_port(projection) - except ValueError: + except (ValueError, PortError): pass receiver = interface_input_port return MappingProjection(sender=sender, receiver=receiver) diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index 4cf413d5b10..855a70fe47a 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -45,7 +45,7 @@ 'DIFFERENCE', 'DIFFERENCE', 'DIFFUSION', 'DIRECT', 'DISABLE', 'DISABLE_PARAM', 'DIST_FUNCTION_TYPE', 'DIST_MEAN', 'DIST_SHAPE', 'DISTANCE_FUNCTION', 'DISTANCE_METRICS', 'DISTRIBUTION_FUNCTION_TYPE', 'DIVISION', 'DRIFT_DIFFUSION_INTEGRATOR_FUNCTION', 'DRIFT_ON_A_SPHERE_INTEGRATOR_FUNCTION', 'DUAL_ADAPTIVE_INTEGRATOR_FUNCTION', - 'EID_SIMULATION', 'EID_FROZEN', 'EITHER', 'ENABLE_CONTROLLER', 'ENABLED', 'ENERGY', 'ENTROPY', + 'EFFERENTS', 'EID_SIMULATION', 'EID_FROZEN', 'EITHER', 'ENABLE_CONTROLLER', 'ENABLED', 'ENERGY', 'ENTROPY', 'EPISODIC_MEMORY_MECHANISM', 'EQUAL', 'ERROR_DERIVATIVE_FUNCTION', 'EUCLIDEAN', 'EVC_MECHANISM', 'EVC_SIMULATION', 'EXAMPLE_FUNCTION_TYPE', 'EXECUTE_UNTIL_FINISHED', 'EXECUTING', 'EXECUTION', 'EXECUTION_COUNT', 'EXECUTION_ID', 'EXECUTION_PHASE', 'EXPONENTIAL', 'EXPONENT', 'EXPONENTIAL_DIST_FUNCTION', 'EXPONENTIAL_FUNCTION', 'EXPONENTS', @@ -77,8 +77,8 @@ 'MECHANISM', 'MECHANISM_COMPONENT_CATEGORY', 'MECHANISM_DEFAULT', 'MECHANISM_DEFAULTInputValue', 'MECHANISM_DEFAULTParams', 'MECHANISM_EXECUTED_LOG_ENTRY', 'MECHANISM_NAME', 'MECHANISM_PARAM_VALUE', 'MECHANISM_TYPE', 'MECHANISM_VALUE', 'MEDIAN', 'METRIC', 'MIN_VAL', 'MIN_ABS_VAL', 'MIN_ABS_INDICATOR', - 'MODE', 'MODULATES','MODULATION', 'MODULATORY_PROJECTION', 'MODULATORY_SIGNAL', 'MODULATORY_SIGNALS', - 'MONITOR', 'MONITOR_FOR_CONTROL', 'MONITOR_FOR_LEARNING', 'MONITOR_FOR_MODULATION', + 'MOD_AFFERENTS', 'MODE', 'MODULATES','MODULATION', 'MODULATORY_PROJECTION', 'MODULATORY_SIGNAL', + 'MODULATORY_SIGNALS', 'MONITOR', 'MONITOR_FOR_CONTROL', 'MONITOR_FOR_LEARNING', 'MONITOR_FOR_MODULATION', 'MODEL_SPEC_ID_GENERIC', 'MODEL_SPEC_ID_INPUT_PORTS', 'MODEL_SPEC_ID_OUTPUT_PORTS', 'MODEL_SPEC_ID_PSYNEULINK', 'MODEL_SPEC_ID_SENDER_MECH', 'MODEL_SPEC_ID_SENDER_PORT', 'MODEL_SPEC_ID_RECEIVER_MECH', 'MODEL_SPEC_ID_RECEIVER_PORT','MODEL_SPEC_ID_PARAMETER_SOURCE', @@ -93,7 +93,7 @@ 'OVERRIDE', 'OVERRIDE_PARAM', 'OVERWRITE', 'OWNER', 'OWNER_EXECUTION_COUNT', 'OWNER_EXECUTION_TIME', 'OWNER_VALUE', 'OWNER_VARIABLE', 'PARAMETER', 'PARAMETER_CIM_NAME', 'PARAMETER_PORT', 'PARAMETER_PORT_PARAMS', 'PARAMETER_PORTS', - 'PARAMETERS', 'PARAMS', 'PARAMS_DICT', 'PATHWAY', 'PATHWAY_PROJECTION', 'PEARSON', + 'PARAMETERS', 'PARAMS', 'PARAMS_DICT', 'PATH_AFFERENTS', 'PATHWAY', 'PATHWAY_PROJECTION', 'PEARSON', 'PORT', 'PORT_COMPONENT_CATEGORY', 'PORT_CONTEXT', 'Port_Name', 'port_params', 'PORT_PREFS', 'PORT_TYPE', 'port_value', 'PORTS', 'PREDICTION_MECHANISM', 'PREDICTION_MECHANISMS', 'PREDICTION_MECHANISM_OUTPUT', 'PREDICTION_MECHANISM_PARAMS', @@ -490,6 +490,10 @@ def _is_metric(metric): CONTROL_SIGNAL = 'ControlSignal' GATING_SIGNAL = 'GatingSignal' +PATH_AFFERENTS = 'path_afferents' +MOD_AFFERENTS = 'mod_afferents' +EFFERENTS = 'efferents' + # Projections: MAPPING_PROJECTION = "MappingProjection" AUTO_ASSOCIATIVE_PROJECTION = "AutoAssociativeProjection" diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index c8129f2d425..5a539d3e725 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -506,8 +506,9 @@ def test_add_conflicting_projection_object(self): proj = MappingProjection(sender=A, receiver=B) with pytest.raises(CompositionError) as error: comp.add_projection(projection=proj, receiver=C) - assert "receiver assignment" in str(error.value) - assert "incompatible" in str(error.value) + assert '"Receiver (\'composition-pytests-C\') assigned to ' \ + '\'MappingProjection from composition-pytests-A[RESULT] to composition-pytests-B[InputPort-0] ' \ + 'is incompatible with the positions of these Components in \'Composition-0\'."' == str(error.value) @pytest.mark.stress @pytest.mark.parametrize( @@ -2913,7 +2914,9 @@ def test_projection_assignment_mistake_swap(self): comp.add_projection(MappingProjection(sender=A, receiver=C), A, C) with pytest.raises(CompositionError) as error_text: comp.add_projection(MappingProjection(sender=B, receiver=D), B, C) - assert "is incompatible with the positions of these Components in the Composition" in str(error_text.value) + assert '"Receiver (\'composition-pytests-C\') assigned to ' \ + '\'MappingProjection from composition-pytests-B[RESULT] to composition-pytests-D[InputPort-0] ' \ + 'is incompatible with the positions of these Components in \'Composition-0\'."' == str(error_text.value) def test_projection_assignment_mistake_swap2(self): # A ----> C -- @@ -2933,8 +2936,9 @@ def test_projection_assignment_mistake_swap2(self): comp.add_projection(MappingProjection(sender=A, receiver=C), A, C) with pytest.raises(CompositionError) as error_text: comp.add_projection(MappingProjection(sender=B, receiver=C), B, D) - - assert "is incompatible with the positions of these Components in the Composition" in str(error_text.value) + assert '"Receiver (\'composition-pytests-D\') assigned to ' \ + '\'MappingProjection from composition-pytests-B[RESULT] to composition-pytests-C[InputPort-0] ' \ + 'is incompatible with the positions of these Components in \'Composition-0\'."' == str(error_text.value) @pytest.mark.composition def test_run_5_mechanisms_2_origins_1_terminal(self, comp_mode): @@ -3165,7 +3169,7 @@ def test_LPP_wrong_component(self): assert ("Bad Projection specification in \'pathway\' arg " in str(error_text.value) and "for add_linear_procesing_pathway method" in str(error_text.value) and "Attempt to assign Projection" in str(error_text.value) - and "to InputPort" in str(error_text.value) + and "using InputPort" in str(error_text.value) and "that is in deferred init" in str(error_text.value)) @pytest.mark.composition diff --git a/tests/ports/test_input_ports.py b/tests/ports/test_input_ports.py index 5f7bc6dcb05..a2c1d807a71 100644 --- a/tests/ports/test_input_ports.py +++ b/tests/ports/test_input_ports.py @@ -125,3 +125,14 @@ def test_default_input(self, default_input): comp.run() # No warning since default_input is set assert m.input_port.value == variable assert m.value == variable + + def test_no_efferents(self): + A = pnl.InputPort() + with pytest.raises(pnl.PortError) as error: + A.efferents + assert '"InputPorts do not have \'efferents\'; (access attempted for Deferred Init InputPort)."' \ + in str(error.value) + with pytest.raises(pnl.PortError) as error: + A.efferents = ['test'] + assert '"InputPorts are not allowed to have \'efferents\' ' \ + '(assignment attempted for Deferred Init InputPort)."' in str(error.value) diff --git a/tests/ports/test_output_ports.py b/tests/ports/test_output_ports.py index 74774381862..6551be99e9b 100644 --- a/tests/ports/test_output_ports.py +++ b/tests/ports/test_output_ports.py @@ -55,3 +55,14 @@ def test_output_port_variable_spec_composition(self, comp_mode): assert np.array_equal(outs, [[3], [2], [1], [1]]) outs = C.run(inputs={mech: [[1.],[2.],[3.]]}, execution_mode=comp_mode) assert np.array_equal(outs, [[3], [2], [1], [2]]) + + def test_no_path_afferents(self): + A = pnl.OutputPort() + with pytest.raises(pnl.PortError) as error: + A.path_afferents + assert '"OutputPorts do not have \'path_afferents\'; (access attempted for Deferred Init OutputPort)."' \ + in str(error.value) + with pytest.raises(pnl.PortError) as error: + A.path_afferents = ['test'] + assert '"OutputPorts are not allowed to have \'path_afferents\' ' \ + '(assignment attempted for Deferred Init OutputPort)."' in str(error.value) diff --git a/tests/ports/test_parameter_ports.py b/tests/ports/test_parameter_ports.py index f294cfd003c..aca2979ae32 100644 --- a/tests/ports/test_parameter_ports.py +++ b/tests/ports/test_parameter_ports.py @@ -70,6 +70,28 @@ def test_direct_call_to_constructor_error(self): ParameterPort(owner='SOMETHING') assert "Contructor for ParameterPort cannot be called directly(context: None" in str(error_text.value) + def test_no_path_afferents(self): + A = TransferMechanism() + with pytest.raises(pnl.PortError) as error: + A.parameter_ports['slope'].path_afferents + assert '"ParameterPorts do not have \'path_afferents\'; (access attempted for TransferMechanism-0[slope])."' \ + in str(error.value) + with pytest.raises(pnl.PortError) as error: + A.parameter_ports['slope'].path_afferents = ['test'] + assert '"ParameterPorts are not allowed to have \'path_afferents\' ' \ + '(assignment attempted for TransferMechanism-0[slope])."' in str(error.value) + + def test_no_efferents(self): + A = TransferMechanism() + with pytest.raises(pnl.PortError) as error: + A.parameter_ports['slope'].efferents + assert '"ParameterPorts do not have \'efferents\'; (access attempted for TransferMechanism-0[slope])."' \ + in str(error.value) + with pytest.raises(pnl.PortError) as error: + A.parameter_ports['slope'].efferents = ['test'] + assert '"ParameterPorts are not allowed to have \'efferents\' ' \ + '(assignment attempted for TransferMechanism-0[slope])."' in str(error.value) + class TestConfigurableParameters: def test_configurable_params(self): old_value = 0.2 @@ -137,6 +159,7 @@ def test_configurable_params(self): assert np.allclose(T.noise.base, new_value) assert np.allclose(T.noise.modulated, new_value) + class TestModParams: def test_mod_param_error(self): T = TransferMechanism() From 906153dcb48aed193f70faa6429e087d9b0d1f4c Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 20 Feb 2022 14:18:18 -0500 Subject: [PATCH 154/285] llvm, component: codestyle Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 47812995aca..d405b7e959a 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1382,7 +1382,8 @@ def _get_compilation_params(self): "monitor_for_control", "state_feature_values", "simulation_ids", "input_labels_dict", "output_labels_dict", "num_estimates", "modulated_mechanisms", "grid", "control_signal_params", - "activation_derivative_fct", "input_specification", "state_feature_specs", + "activation_derivative_fct", "input_specification", + "state_feature_specs", # Reference to other components "objective_mechanism", "agent_rep", "projections", # Shape mismatch @@ -1395,7 +1396,7 @@ def _get_compilation_params(self): # not used in computation "has_recurrent_input_port", "enable_learning", "enable_output_type_conversion", "changes_shape", - "output_type", 'bounds' } + "output_type", "bounds"} # Mechanism's need few extra entires: # * matrix -- is never used directly, and is flatened below # * integration rate -- shape mismatch with param port input From e0d2cf508abc1281f6a6b7c122746f1374ee890a Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 20 Feb 2022 14:19:33 -0500 Subject: [PATCH 155/285] llvm, component: Drop 'has_initializers' from components other than mechanism In reality we only use it in RTM. Reduces space needed for RO parameters: predator-prey: 7.73kB -> 5.96kB stability-flexibility: 8.84kB -> 5.75kB Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 2 ++ psyneulink/core/llvm/execution.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index d405b7e959a..b414b426378 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1405,6 +1405,8 @@ def _get_compilation_params(self): else: # Execute until finished is only used by mechanisms blacklist.update(["execute_until_finished", "max_executions_before_finished"]) + # "has_initializers" is only used by RTM + blacklist.update(["has_initializers"]) def _is_compilation_param(p): if p.name not in blacklist and not isinstance(p, (ParameterAlias, SharedParameter)): diff --git a/psyneulink/core/llvm/execution.py b/psyneulink/core/llvm/execution.py index 1f54c69072b..ff7a2defdd6 100644 --- a/psyneulink/core/llvm/execution.py +++ b/psyneulink/core/llvm/execution.py @@ -132,7 +132,10 @@ def _bin_func_multirun(self): def upload_ctype(self, data, name='other'): self._uploaded_bytes[name] += ctypes.sizeof(data) - assert ctypes.sizeof(data) != 0 + if ctypes.sizeof(data) == 0: + # 0-sized structures fail to upload + # provide a small device buffer instead + return jit_engine.pycuda.driver.mem_alloc(4) return jit_engine.pycuda.driver.to_device(bytearray(data)) def download_ctype(self, source, ty, name='other'): From 8f3770d87151f4c6575c33932b17f2c651d77db1 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 20 Feb 2022 19:13:55 -0500 Subject: [PATCH 156/285] llvm, mechanism: Do not copy base parameter values if all will be overwritten Signed-off-by: Jan Vesely --- psyneulink/core/components/mechanisms/mechanism.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 9fc6cbe3796..5f4c92f708f 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2971,7 +2971,8 @@ def _gen_llvm_param_ports_for_obj(self, obj, params_in, ctx, builder, # Allocate a shadow structure to overload user supplied parameters params_out = builder.alloca(params_in.type.pointee, name="modulated_parameters") - builder = pnlvm.helpers.memcpy(builder, params_out, params_in) + if len(param_ports) != len(obj.llvm_param_ids): + builder = pnlvm.helpers.memcpy(builder, params_out, params_in) def _get_output_ptr(b, i): ptr = pnlvm.helpers.get_param_ptr(b, obj, params_out, From a730a299d0911f936c0358e85feab038c244614e Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 13 Feb 2022 00:15:53 -0500 Subject: [PATCH 157/285] llvm, mechanism: Use base parameter structure to create all modulated parameters The original approach created a copy to modulate mechanism parameters (if needed to apply mech parameter ports) and then passed this copy to internal function invocation. Invoking internal functions would then create more copies (if needed) to apply parameter ports of function parameters. The new approach passes the mechanism base parameters, so the copies of function parameters can be made from the original. The overall amount of copied data is the same, but now the same shared source is used for all copies This is especially beneficial for GPUs that place the shared parameters in high BW on-chip memories. The observed effect for stability-flexibility model is ~20% reduction in the total amount of data read from thread private memory, Resulting in ~10% improvement in kernel execution time. Measured on P620 GPU. Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 51 ++++++++++--------- .../processing/transfermechanism.py | 22 ++++---- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 5f4c92f708f..736114839c6 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -3040,67 +3040,66 @@ def _fill_input(b, s_input, i): mech_params, mech_state, mech_in) return builder - def _gen_llvm_invoke_function(self, ctx, builder, function, params, state, variable, *, tags:frozenset): + def _gen_llvm_invoke_function(self, ctx, builder, function, f_params, f_state, variable, *, tags:frozenset): fun = ctx.import_llvm_function(function, tags=tags) fun_out = builder.alloca(fun.args[3].type.pointee, name=function.name + "_output") - builder.call(fun, [params, state, variable, fun_out]) + builder.call(fun, [f_params, f_state, variable, fun_out]) return fun_out, builder - def _gen_llvm_is_finished_cond(self, ctx, builder, params, state): + def _gen_llvm_is_finished_cond(self, ctx, builder, m_params, m_state): return ctx.bool_ty(1) - def _gen_llvm_mechanism_functions(self, ctx, builder, params, state, arg_in, + def _gen_llvm_mechanism_functions(self, ctx, builder, m_base_params, m_params, m_state, arg_in, ip_output, *, tags:frozenset): # Default mechanism runs only the main function - f_params_ptr = pnlvm.helpers.get_param_ptr(builder, self, params, "function") + f_base_params = pnlvm.helpers.get_param_ptr(builder, self, m_base_params, "function") f_params, builder = self._gen_llvm_param_ports_for_obj( - self.function, f_params_ptr, ctx, builder, params, state, arg_in) - f_state = pnlvm.helpers.get_state_ptr(builder, self, state, "function") + self.function, f_base_params, ctx, builder, m_base_params, m_state, arg_in) + f_state = pnlvm.helpers.get_state_ptr(builder, self, m_state, "function") return self._gen_llvm_invoke_function(ctx, builder, self.function, f_params, f_state, ip_output, tags=tags) - def _gen_llvm_function_internal(self, ctx, builder, params, state, arg_in, - arg_out, *, tags:frozenset): + def _gen_llvm_function_internal(self, ctx, builder, m_params, m_state, arg_in, + arg_out, m_base_params, *, tags:frozenset): ip_output, builder = self._gen_llvm_input_ports(ctx, builder, - params, state, arg_in) + m_base_params, m_state, arg_in) - value, builder = self._gen_llvm_mechanism_functions(ctx, builder, params, - state, arg_in, - ip_output, - tags=tags) + value, builder = self._gen_llvm_mechanism_functions(ctx, builder, m_base_params, + m_params, m_state, arg_in, + ip_output, tags=tags) # Update execution counter - exec_count_ptr = pnlvm.helpers.get_state_ptr(builder, self, state, "execution_count") + exec_count_ptr = pnlvm.helpers.get_state_ptr(builder, self, m_state, "execution_count") exec_count = builder.load(exec_count_ptr) exec_count = builder.fadd(exec_count, exec_count.type(1)) builder.store(exec_count, exec_count_ptr) # Update internal clock (i.e. num_executions parameter) - num_executions_ptr = pnlvm.helpers.get_state_ptr(builder, self, state, "num_executions") + num_executions_ptr = pnlvm.helpers.get_state_ptr(builder, self, m_state, "num_executions") for scale in [TimeScale.TIME_STEP, TimeScale.PASS, TimeScale.TRIAL, TimeScale.RUN]: num_exec_time_ptr = builder.gep(num_executions_ptr, [ctx.int32_ty(0), ctx.int32_ty(scale.value)]) new_val = builder.load(num_exec_time_ptr) new_val = builder.add(new_val, new_val.type(1)) builder.store(new_val, num_exec_time_ptr) - builder = self._gen_llvm_output_ports(ctx, builder, value, params, state, arg_in, arg_out) + builder = self._gen_llvm_output_ports(ctx, builder, value, m_base_params, m_state, arg_in, arg_out) - val_ptr = pnlvm.helpers.get_state_ptr(builder, self, state, "value") + val_ptr = pnlvm.helpers.get_state_ptr(builder, self, m_state, "value") if val_ptr.type.pointee == value.type.pointee: - pnlvm.helpers.push_state_val(builder, self, state, "value", value) + pnlvm.helpers.push_state_val(builder, self, m_state, "value", value) else: # FIXME: Does this need some sort of parsing? warnings.warn("Shape mismatch: function result does not match mechanism value param: {} vs. {}".format(value.type.pointee, val_ptr.type.pointee)) # is_finished should be checked after output ports ran is_finished_f = ctx.import_llvm_function(self, tags=tags.union({"is_finished"})) - is_finished_cond = builder.call(is_finished_f, [params, state, arg_in, + is_finished_cond = builder.call(is_finished_f, [m_params, m_state, arg_in, arg_out]) return builder, is_finished_cond @@ -3134,11 +3133,11 @@ def _gen_llvm_function(self, *, extra_args=[], ctx:pnlvm.LLVMBuilderContext, tag builder.ret(finished) return builder.function - def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, *, tags:frozenset): + def _gen_llvm_function_body(self, ctx, builder, base_params, state, arg_in, arg_out, *, tags:frozenset): assert "reset" not in tags params, builder = self._gen_llvm_param_ports_for_obj( - self, params, ctx, builder, params, state, arg_in) + self, base_params, ctx, builder, base_params, state, arg_in) is_finished_flag_ptr = pnlvm.helpers.get_state_ptr(builder, self, state, "is_finished_flag") @@ -3164,17 +3163,19 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, # Get internal function args_t = [a.type for a in builder.function.args] + args_t[4:4] = [base_params.type] internal_builder = ctx.create_llvm_function(args_t, self, name=builder.function.name + "_internal", return_type=ctx.bool_ty) - iparams, istate, iin, iout = internal_builder.function.args[:4] + iparams, istate, iin, iout, ibase_params = internal_builder.function.args[:5] internal_builder, is_finished = self._gen_llvm_function_internal(ctx, internal_builder, - iparams, istate, iin, iout, tags=tags) + iparams, istate, iin, iout, + ibase_params, tags=tags) internal_builder.ret(is_finished) # Call Internal Function internal_f = internal_builder.function - is_finished_cond = builder.call(internal_f, [params, state, arg_in, arg_out, *builder.function.args[4:]]) + is_finished_cond = builder.call(internal_f, [params, state, arg_in, arg_out, base_params, *builder.function.args[4:]]) #FIXME: Flag and count should be int instead of float # Check if we reached maximum iteration count diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index 10da9fba668..cd0fc1bcb39 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -1603,32 +1603,32 @@ def _gen_llvm_is_finished_cond(self, ctx, builder, params, state): cmp_str = self.parameters.termination_comparison_op.get(None) return builder.fcmp_ordered(cmp_str, cmp_val, threshold) - def _gen_llvm_mechanism_functions(self, ctx, builder, params, state, arg_in, - ip_out, *, tags:frozenset): + def _gen_llvm_mechanism_functions(self, ctx, builder, m_base_params, m_params, + m_state, arg_in, ip_out, *, tags:frozenset): if self.integrator_mode: - if_state = pnlvm.helpers.get_state_ptr(builder, self, state, + if_state = pnlvm.helpers.get_state_ptr(builder, self, m_state, "integrator_function") - if_param_ptr = pnlvm.helpers.get_param_ptr(builder, self, params, - "integrator_function") + if_base_params = pnlvm.helpers.get_param_ptr(builder, self, m_base_params, + "integrator_function") if_params, builder = self._gen_llvm_param_ports_for_obj( - self.integrator_function, if_param_ptr, ctx, builder, - params, state, arg_in) + self.integrator_function, if_base_params, ctx, builder, + m_base_params, m_state, arg_in) mf_in, builder = self._gen_llvm_invoke_function( ctx, builder, self.integrator_function, if_params, if_state, ip_out, tags=tags) else: mf_in = ip_out - mf_state = pnlvm.helpers.get_state_ptr(builder, self, state, "function") - mf_param_ptr = pnlvm.helpers.get_param_ptr(builder, self, params, "function") + mf_state = pnlvm.helpers.get_state_ptr(builder, self, m_state, "function") + mf_base_params = pnlvm.helpers.get_param_ptr(builder, self, m_base_params, "function") mf_params, builder = self._gen_llvm_param_ports_for_obj( - self.function, mf_param_ptr, ctx, builder, params, state, arg_in) + self.function, mf_base_params, ctx, builder, m_base_params, m_state, arg_in) mf_out, builder = self._gen_llvm_invoke_function(ctx, builder, self.function, mf_params, mf_state, mf_in, tags=tags) - clip_ptr = pnlvm.helpers.get_param_ptr(builder, self, params, "clip") + clip_ptr = pnlvm.helpers.get_param_ptr(builder, self, m_params, "clip") if len(clip_ptr.type.pointee) != 0: assert len(clip_ptr.type.pointee) == 2 clip_lo = builder.load(builder.gep(clip_ptr, [ctx.int32_ty(0), From 7b4abd2709be523ab06c62fa0940ef10e9d7037f Mon Sep 17 00:00:00 2001 From: jdcpni Date: Tue, 22 Feb 2022 11:16:29 -0500 Subject: [PATCH 158/285] Fix/ocm/state features for nested comps (#2329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • optimizationcontrolmechanism.py - _gen_llvm_evaluate_function: num_estimates -> num_trials_per_estimate * - * - * - * - * • composition.py - __init__: moved controller instantiation until after nodes, projections and pathways * • composition.py - __init__: restored add_controller position * llvm/struct generation: Make sure num_estimats per trial is always integer Signed-off-by: Jan Vesely * - * • composition.py: - _update_controller: added - add_controller and _analyze_graph(): call _update_controller * - * • composition.py _update_controller: fixed to loop through all input_ports of comp INPUT nodes * • test_control.py - test_agent_rep_assignement_as_controller_and_replacement: updated to test that shadowing projections to state_input_ports are properly added and deleted * • optimizationfunctions.py: - _function: refactored to put use aggregation_function at end - _grid_evaluate: still needs to return all_samples * - * • composition.py - added call to _update_controller to add_node - moved test for projections to controller.state_input_ports to run() * - * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py: moved calls to _update_controller to _complete_init_of_partially_initialized_nodes moved _update_controller to ocm._update_state_input_ports _instantiate_controller_shadow_projections [still needs to be implemented] • optimizationcontrolmechanism.py: added _update_state_input_ports [**still needed work**] * • composition.py added needs_update_controller * - * • composition.py: - implemented self.needs_update_controller - moved implementation of controlsignal projections from add_controller to _instantiate_control_projections that is called in _complete_init_of_partially_initialized_nodes Note: still need to set self.needs_update_controller to False after instantiating state_input_ports and projections to them * - * - * - * - * - * - * - * - * • Passing all test_control tests except test_mode_based_num_estimates * • Passing all test_control tests * - * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: handle nested input nodes * - * • optimizationcontrolmechanism.py _update_state_input_ports_for_controller: fixed bug with > 1 INPUT node in Composition * • test_show_graph.py: passes all tests * - * • test_report.py: passing all tests * • Passes all tests! * - * - * • composition.py: reorganize with #region and #enregions * • composition.py: reorganize with #region and #enregions * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * - * - * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * - * - * • port.py: - refactor to eliminate: - efferents attribute from InputPorts and Parameters - path_afferents attribute from OutputPports - add remove_projections() - add mod_afferents property - _get_input_struct_type(): add try and accept for path_afferents • inputport.py: - add path_afferents override • parameterport.py: - add path_afferents override • outputport.py: - add efferents override • composition.py: - add_projection(): call port.remove_projection • keywords.py: add PATH_AFFERENTS, MOD_AFFERENTS, EFFERENTS * - * • Passes all tests * - * • test_input_ports.py: - add test_no_efferents() * • test_output_ports.py: - add test_no_path_afferents() * • test_parameter_ports.py: - add test_no_path_afferents() - add test_no_efferents() * - * - * • optimizationcontrolmechanism.py: - clean-up of state features for CFA agent_rep * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods - BEFORE ADDITIONAL MODIFICATION OF HANDLING OF DEFERRED INITS * • optimizationcontrolmechanism.py: MODIFICATION OF HANDLING OF DEFERRED INITS: - _update_state_feature_dicts() - state_features * - * PASSES ALL TESTS • optimizationcontrolmechanism.py: - state_features: enforce that all keys are mechanisms, including ones in nest comps * PASSES ALL TESTS • optimizationcontrolmechanism.py: update docstring * - * - * - * - Co-authored-by: Jan Vesely Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- .../control/optimizationcontrolmechanism.py | 172 +++++++++++------- psyneulink/core/compositions/composition.py | 32 +--- tests/composition/test_control.py | 129 ++++++------- 3 files changed, 186 insertions(+), 147 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 4efa968ecbe..da8e97bc5fe 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -228,11 +228,13 @@ .. _OptimizationControlMechanism_Agent_Rep_Composition: - **state_features** for an agent_rep that is a* **Composition** + **state_features** *for an agent_rep that is a* **Composition** | - The **state_features** specify the inputs to the Composition assigned as the `agent_rep + .. _OptimizationControlMechanism_State_Features_Automatic_Assignment: + + *Automatic assignment.* The **state_features** specify the inputs to the Composition assigned as the `agent_rep ` when it is executed by the OptimizationControlMechanism to `evaluate ` its performance. The default is for the evaluation to use the same values received by the `agent_rep ` as its `external inputs @@ -245,18 +247,18 @@ | .. _OptimizationControlMechanism_State_Features_Explicit_Specification: - The **state_features** argument can also be specified explicitly, using the formats described below. This is - useful if values other than the `external inputs ` to the `agent_rep - ` Composition are to be used to evaluate it, to restrict evaluation - This allows values other than the `external inputs ` to the `agent_rep - ` Composition to be used to evaluate it; to restrict evaluation to a - subset of inputs (while others are held constant); and/or to assign specific functions to one or more - `state_input_ports ` that allow them to process inputs - (e.g., modulate and/or intergrate them) before them as `state_feature_values state_feature_values - ` (see `below - `). Note that assigning **state_features** explicitly - overrides their automatic assignment, so that all required values must be specified, and this must be done - accurate, as described below. + + *Explicit specificaiont.* The **state_features** argument can also be specified explicitly, using the formats + described below. This is useful if values other than the `external inputs ` to + the `agent_rep ` Composition are to be used to evaluate it, to restrict + evaluation This allows values other than the `external inputs ` to the `agent_rep + ` Composition to be used to evaluate it; to restrict evaluation to a subset + of inputs (while others are held constant); and/or to assign specific functions to one or more `state_input_ports + ` that allow them to process inputs (e.g., modulate and/or integrate + them) before them as `state_feature_values state_feature_values ` + (see `below `). Note that assigning **state_features** + explicitly overrides their automatic assignment, so that all required values must be specified, and this must be + done accurate, as described below. .. _OptimizationControlMechanism_State_Features_Shapes: @@ -311,14 +313,19 @@ .. _Optimization_Control_Mechanism_State_Feature_List_Inputs: * *List* -- a list of individual input source specifications, that can be any of the forms of individual input - specifications listed `below `. The items + specifications listed `below `. The items must be listed in the order that `INPUT ` Nodes are listed in the `agent_rep `\\'s `nodes ` attribute (and returned by its `get_nodes_by_role(NodeRole.INPUT) ` method). If the list is incomplete, the remaining INPUT Nodes are assigned their `default variable ` as input when the `agent_rep - `\\'s `evaluate ` method is called; ``None`` can + `\\'s `evaluate ` method is called; ``None`` can be used as an entry to "skip" items in the list (i.e., specify that they receive their `default variable - ` as input). + ` as input). Items can be specified in the list that have not yet been added to the + OptimizationControlMechanism's Composition or its `agent_rep `, that are + either sources of input to `agent_rep `\\'s `INPUT ` + `Nodes `, or those Nodes themselves. However, these must be added before Composition is + executed, and must appear in the list in the same position that the `INPUT Nodes to which they pertain are + list in the `agent_rep `\\'s `nodes ` attribute. .. _Optimization_Control_Mechanism_State_Feature_Set_Inputs: @@ -351,12 +358,14 @@ method is executed. .. _Optimization_Control_Mechanism_Numeric_State_Feature: + * *numeric value* -- create an `InputPort` with the specified value as its `default variable ` and no `afferent Projections `; as a result, the specified value is assigned as the input to the corresponding `INPUT ` `Node ` of the `agent_rep ` each time it is `evaluated `. .. _Optimization_Control_Mechanism_Tuple_State_Feature: + * *2-item tuple* -- the first item must be a `Port` or `Mechanism` specification, as described below; the second item must be a `Function`, that is assigned as the `function ` of the corresponding `state_input_port `; @@ -366,12 +375,14 @@ ` for additional details). .. _Optimization_Control_Mechanism_Input_Port_Dict_State_Feature: + * *specification dictionary* -- an `InputPort specification dictionary ` can be used to configure the corresponding `state_input_port `, if `Parameters ` other than its `function ` need to be specified (e.g., its `name ` or more than a single `afferent Projection `). .. _Optimization_Control_Mechanism_Input_Port_State_Feature: + * *InputPort specification* -- creates an `InputPort` that `shadows ` the input to the specified InputPort, the `value ` of which is used as the corresponding value of the OptimizationControlMechanism's `state_feature_values `. @@ -395,6 +406,7 @@ The InputPorts specified as state_features are marked as `internal_only ` = `True`. .. _Optimization_Control_Mechanism_Output_Port_State_Feature: + * *OutputPort specification* -- this creates an `InputPort` that receives a `MappingProjection` from the specified `OutputPort`; it can be any form of `OutputPort specification ` for any `OutputPort` of another `Mechanism ` in the Composition. The `value ` @@ -410,12 +422,7 @@ Mechanism's input_port, as described `above `. If the Mechanism is in a `nested Composition `, it must be an `INPUT ` `Node ` of that Composition (see note above). If its OutputPort needs to be used, it must be - specified explicitly (as described `above `). In - contrast, if the `agent_rep ` is a `CompositionFunctionApproximator`, - then the Mechanism's `primary OutputPort ` is used (since that is typical usage, and there - are no assumptions made about the state features of a `CompositionFunctionApproximator`); if the input to the - Mechanism *is* to be shadowed, then its InputPort must be specified explicitly (as described `above - `). + specified explicitly (as described `above `). | @@ -425,22 +432,35 @@ | - **state_features** specify the **feature_values** - argument to the CompositionFunctionApproximator's `evaluate ` method. - These cannot be determined automatically and so they *must be specified explicity*, in a list, with the correct - number of items in the same order and with the same shapes they are expected have in the array passed to the - **feature_values** argument of the `evaluate` method (see warning below). + The **state_features** specify the **feature_values** argument to the `CompositionFunctionApproximator`\\'s + `evaluate ` method. These cannot be determined automatically and so + they *must be specified explicitly*, in a list, with the correct number of items in the same order and with + the same shapes they are expected have in the array passed to the **feature_values** argument of the + `evaluate ` method (see warning below). + .. warning:: The **state_features** for an `agent_rep ` that is a `CompositionFunctionApproximator` cannot be created automatically nor can they be validated; thus specifying the wrong number or invalid **state_features**, or specifying them in an incorrect order may produce errors that are unexpected or difficult to interpret. + The list of specifications can contain any of the forms of specification used for an `agent_rep + ` that is a Composition as described `above + `, with the following exception: if a + `Mechanism` is specified, its `primary OutputPort ` is used (rather than + shadowing its primary InputPort), since that is more typical usage, and there are no assumptions + made about the state features of a `CompositionFunctionApproximator` (as there are about a Composition + as `agent_rep `); if the input to the Mechanism *is* to be + `shadowed `, then its InputPort must be specified explicitly (as described + `above `). + COMMENT: FIX: CONFIRM THAT THE FOLLOWING WORKS State features can also be added to an existing OptimizationControlMechanism using its `add_state_features` method. COMMENT +| + .. _OptimizationControlMechanism_State_Feature_Function_Arg: * **state_feature_function** -- specifies a `function ` to be used as the default @@ -1101,7 +1121,7 @@ class OptimizationControlMechanism(ControlMechanism): specifies the Components from which `state_input_ports ` receive their inputs, the `values ` of which are assigned to `state_feature_values ` and provided as input to the `agent_rep - 's `evaluate ` method whent it is executed. + 's `evaluate ` method when it is executed. See `state_features ` for details of specification. state_feature_function : Function or function : default None @@ -1200,14 +1220,27 @@ class OptimizationControlMechanism(ControlMechanism): of Optimization ` for additional details). state_features : Dict[Node:source] - dictionary listing the `INPUT ` `Nodes ` of `agent_rep - ` (keys) and the source of their inputs (values) - as specified in **state_features** (or determined automatically), and used to construct `state_input_ports - `, the values of which are assigned to - `state_feature_values ` and provided as - input to the `agent_rep 's `evaluate ` - method when it is executed (see `state_features ` - and `OptimizationControlMechanism_State_Features` for additional details). + dictionary in which keys are Mechanism's that are `INPUT ` `Nodes ` of + `agent_rep ` and/or any `nested Compositions ` + within it, and values are sources of input specified in **state_features** (or determined automatically). The + latter are provided as the inputs to `state_input_ports `, the + values of which are assigned to `state_feature_values ` and + provided as input to the `agent_rep 's `evaluate ` + method when it is executed (see `state_features ` and + `OptimizationControlMechanism_State_Features` for additional details). + + .. technical_note:: + the state_features dict is used by the _build_predicted_inputs() method of an `agent_rep + ` Composition to construct inputs for its `evaluate + ` method. Only Mechanisms are used as keys, to accommodate the possibility + that some but not all of the `INPUT ` `Nodes ` of any nested + composition(s) are specified in the `state_features ` + of the OptimizationControlMechanism's constructor, allowing _build_predicted_inputs() to identify and + provide defaults for any that are not specified. Accordingly, if **state_features** is not specified in + the constructor, and thus assigned automatically, the state_features dictionary will contain entries for + shadowing the InputPorts of all Mechanisms that are `INPUT ` `Nodes ` + in either `agent_rep ` and/or of any Compositions nested at any + level within it. state_feature_values : 2d array the current value of each item of the OptimizationControlMechanism's `state_input_ports @@ -1992,10 +2025,13 @@ def _parse_specs(state_feature_specs, spec_str="list"): node = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner node_name = node.name else: + # FIX 2/18/22 - FALSELY ASSUMES THAT SPEC IN LIST IS FOR AN INPUT NODE RATHER THAN JUST ITS SOURCE + # SHOULD INSTEAD USE A PLACEMARKER, AND ASSIGN ONCE IT IS THERE IN + # _update_state_input_ports_for_controller() ?AND _update_state_features_dict()? # Node not (yet) in agent_rep, so uses its node name spec = state_feature_specs[i] - node = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner - node_name = node.name + node = None + node_name = f'DEFFERED {str(i-num_nodes)}' # SPEC # Assign specs # Only process specs for which there are already INPUT Nodes in agent_rep @@ -2186,19 +2222,26 @@ def _parse_state_feature_function(self, feature_function): return feature_function def _update_state_features_dict(self): - # FIX: 1/30/22 - ??REFACTOR TO USE Composition aux_components?? - # OR IMPLEMENT LIST WITH DEFERRED ITEMS STORED THERE (THAT WAY DON'T HAVE TO RELY ON NAME BELOW) - # OR IMPLEMENT INTERNAL _state_features dict THEN state_features AS A PROPERTY - # THAT TRACKS state_input_ports AS BEFORE - # FIX: MODIFY THIS TO INDICATE WHICH SPECS ARE STILL MISSING - # FIX: HANDLE ERRORS HERE INSTEAD OF _validate_state_features OR EXECUTE THAT FIRST AND CAPTURE HERE + agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) + specified_input_nodes = self._specified_input_nodes_in_order + for i, port in enumerate(self.state_input_ports): - node = self._specified_input_nodes_in_order[i] + # Get value (need first, to determine whether it belongs to a nested Comp, for assigning key) feature = self.state_feature_specs[i] + # Get INPUT Node of agent_rep as key: + if (isinstance(feature, Component) and + feature.owner in [n[0] for n in self.agent_rep._get_nested_nodes()]): + node = feature.owner + elif specified_input_nodes[i]: + node = specified_input_nodes[i] + elif i < len(agent_rep_input_nodes): + node = specified_input_nodes[i] = agent_rep_input_nodes[i] + else: + node = None if not (isinstance(node, str) and 'DEFERRED' in node): continue - if feature.owner not in self._get_agent_rep_input_nodes(): - # assert False, f"PROGRAM ERROR: Node not in {self.agent_rep.name} should have been caught above." + if feature.owner not in self._get_agent_rep_input_nodes(comp_as_node=ALL): + # Don't add to dict, will be dealt with or raise an error at run time continue self.state_feature_specs[i] = feature @@ -2294,11 +2337,8 @@ def _update_state_input_ports_for_controller(self, context=None): # Assign OptimizationControlMechanism attributes self.state_input_ports.data = state_input_ports - self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) - self.parameters.state_feature_specs.set([input_port.shadow_inputs - for input_port in self.state_input_ports], - override=True) - self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=True) + self._num_state_feature_specs = len(self.state_input_ports) + self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=False) self.parameters.state_feature_specs.set([input_port.shadow_inputs for input_port in self.state_input_ports], override=True) return True @@ -2331,6 +2371,9 @@ def _validate_state_features(self): state_feature_specs = user_specs if isinstance(state_feature_specs, list): + # FIX: 2/20/22: CONSIDER ALLOWING PARTIAL SPECIFICATION OF INPUT NODES IN NESTED COMPS: + # NEED TO DETERMINE WHETHER NODE IS NESTED AND, IF SO, AND ONLY PARTIAL + # FLESH OUT SPEC FOR REST OF NEST COMP # Convert list to dict, assuming list is in order of INPUT Nodes, # and assigning the corresponding INPUT Nodes as keys for use in comp._build_predicted_inputs_dict() input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) @@ -2403,14 +2446,11 @@ def _validate_state_features(self): raise OptimizationControlMechanismError( self_has_state_features_str + f"({[d.name for d in invalid_state_features]}) " + not_in_comps_str) - # # FOR DEBUGGING: (TO SEE CODING ERRORS DIRECTLY) + # # FOLLOWING IS FOR DEBUGGING: (TO SEE CODING ERRORS DIRECTLY) ----------------------- + # print("****** DEBUGGING CODE STILL IN OCM -- REMOVE FOR PROPER TESTING ************") # inputs = self.agent_rep._build_predicted_inputs_dict(None, self) # inputs_dict, num_inputs = self.agent_rep._parse_input_dict(inputs) - # if len(self.state_input_ports) < len(inputs_dict): - # warnings.warn(f"The '{STATE_FEATURES}' specified for '{self.name}' are legal, but there are fewer " - # f"than the number of input_nodes for its {AGENT_REP} ('{self.agent_rep.name}'); " - # f"the remaining inputs will be assigned default values. Use the {AGENT_REP}'s " - # f"get_inputs_format() method to see the format for its inputs.") + # # END DEBUGGING --------------------------------------------------------------------- # Ensure state_features are compatible with input format for agent_rep Composition try: @@ -3105,9 +3145,17 @@ def num_state_input_ports(self): @property def state_features(self): - agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) - state_features_dict = {(k if k in agent_rep_input_nodes else f"{k.name} DEFERRED"):v - for k,v in zip(self._specified_input_nodes_in_order, self.state_feature_specs)} + self._update_state_features_dict() + agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=ALL) + state_features_dict = {} + # Use num_state_feature_specs here instead of num_state_input_ports as there may be some "null" (None) specs + for i in range(self._num_state_feature_specs): + # Assign keys as INPUT Nodes of agent_rep + if self._specified_input_nodes_in_order[i] in agent_rep_input_nodes: + k = self._specified_input_nodes_in_order[i] + else: + k = f"EXPECTED INPUT NODE {i} OF {self.agent_rep.name}" + state_features_dict[k] = self.state_feature_specs[i] return state_features_dict @property diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 504925bac31..e498ebf8b51 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -8042,9 +8042,6 @@ def _check_nodes_initialization_status(self, context=None): f"or a composition nested within it." ) - # FIX: 11/3/21 ??GET RID OF THIS AND CALL TO IT ONCE PROJECTIONS HAVE BEEN IMPLEMENTED FOR SHADOWED INPUTS - # CHECK WHETHER state_input_ports ADD TO OR REPLACE shadowed_inputs - # FIX: 1/28/22 - NEED TO ACCOMODATE None OR MISSING state_feature_values, EITHER HERE OR IN predicted_inputs def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): """Format predict_inputs from controller as input to evaluate method used to execute simulations of Composition. @@ -8053,14 +8050,10 @@ def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): Deal with inputs for nodes in nested Compositions """ controller = controller or self.controller - # FIX: 1/29/22 - REFACTOR TO USE OCM.state_features DICT? # Use keys for inputs dict from OptimizationControlMechanism state_features if it is specified as a dict - # (unless it has SHADOW_INPUTS entry, in which case that is handled below) - # # MODIFIED 1/29/22 OLD: - # input_dict_keys = controller.agent_rep.get_nodes_by_role(NodeRole.INPUT)[:len(controller.state_input_ports)] - # MODIFIED 1/29/22 NEW: + # Note: these are always Mechanisms, including those that are INPUT Nodes in nested Compositions; + # this is so that if any INPUT Nodes of a nested Composition are unspecified, defaults can be assigned input_dict_keys = list(controller.state_features.keys()) - # MODIFIED 1/29/22 END inputs = {} no_predicted_input = (predicted_inputs is None or not len(predicted_inputs)) @@ -8068,14 +8061,13 @@ def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): warnings.warn(f"{self.name}.evaluate() called without any inputs specified; default values will be used") nested_nodes = dict(self._get_nested_nodes()) - # FIX: 11/3/21 NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED - shadow_inputs_start_index = controller.num_outcome_input_ports - for j in range(len(controller.input_ports) - shadow_inputs_start_index): - input_port = controller.input_ports[j + shadow_inputs_start_index] + + for i in range(controller.num_state_input_ports): + input_port = controller.state_input_ports[i] if no_predicted_input: predicted_input = input_port.default_input_shape else: - predicted_input = predicted_inputs[j] + predicted_input = predicted_inputs[i] # Shadow input specified if hasattr(input_port, SHADOW_INPUTS) and input_port.shadow_inputs is not None: @@ -8117,7 +8109,7 @@ def _get_enclosing_comp_for_node(input_port, comp): if isinstance(shadow_input_owner, CompositionInterfaceMechanism): shadow_input_owner = shadow_input_owner.composition # Use key from OptimizationControlMechanism state_features dict if specified, else the source node - key = input_dict_keys[j] if input_dict_keys else shadow_input_owner + key = input_dict_keys[i] if input_dict_keys else shadow_input_owner inputs[key] = predicted_input # Regular input specified (i.e., projection from an OutputPort) @@ -8126,7 +8118,7 @@ def _get_enclosing_comp_for_node(input_port, comp): if input_port.path_afferents: source = input_port.path_afferents[0].sender.owner # Use key from OptimizationControlMechanism state_features dict if specified, else the source node - key = input_dict_keys[j] if input_dict_keys else source + key = input_dict_keys[i] if input_dict_keys else source inputs[key] = predicted_input return inputs @@ -10899,15 +10891,7 @@ def _build_variable_for_input_CIM(self, inputs): origin_node = origin_node.composition if origin_node in inputs: - # MODIFIED 12/19/21 OLD: value = inputs[origin_node][index] - # # MODIFIED 12/19/21 NEW: - # # MODIFIED 12/19/21 END - # if origin_node.input_labels_dict: - # labels = origin_node.input_labels_dict - # else: - # value = inputs[origin_node][index] - else: value = origin_node.defaults.variable[index] diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 59a7fd181b2..33b9f1f792b 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -133,7 +133,9 @@ def test_bad_objective_mechanism_spec(self): assert expected_error in error_msg def test_objective_mechanism_spec_as_monitor_for_control_error(self): - expected_error = 'The \'monitor_for_control\' arg of \'ControlMechanism-0\' contains a specification for an ObjectiveMechanism ([(ObjectiveMechanism ObjectiveMechanism-0)]). This should be specified in its \'objective_mechanism\' argument.' + expected_error = 'The \'monitor_for_control\' arg of \'ControlMechanism-0\' contains a specification ' \ + 'for an ObjectiveMechanism ([(ObjectiveMechanism ObjectiveMechanism-0)]). ' \ + 'This should be specified in its \'objective_mechanism\' argument.' with pytest.raises(pnl.ControlMechanismError) as error: pnl.Composition(controller=pnl.ControlMechanism(monitor_for_control=pnl.ObjectiveMechanism())) error_msg = error.value.error_value @@ -148,46 +150,44 @@ def test_deferred_init(self, control_spec): Input = pnl.TransferMechanism(name='Input') reward = pnl.TransferMechanism(output_ports=[pnl.RESULT, pnl.MEAN, pnl.VARIANCE], name='reward') - Decision = pnl.DDM(function=pnl.DriftDiffusionAnalytical(drift_rate=(1.0, - pnl.ControlProjection(function=pnl.Linear, - control_signal_params={ - pnl.ALLOCATION_SAMPLES: np.arange( - 0.1, - 1.01, - 0.3)})), - threshold=(1.0, - pnl.ControlProjection(function=pnl.Linear, - control_signal_params={ - pnl.ALLOCATION_SAMPLES: - np.arange( - 0.1, - 1.01, - 0.3)})), - noise=0.5, - starting_point=0, - t0=0.45), - output_ports=[pnl.DECISION_VARIABLE, - pnl.RESPONSE_TIME, - pnl.PROBABILITY_UPPER_THRESHOLD], - name='Decision') + Decision = pnl.DDM( + function=pnl.DriftDiffusionAnalytical(drift_rate=(1.0, + pnl.ControlProjection( + function=pnl.Linear, + control_signal_params={ + pnl.ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3) + })), + threshold=(1.0, + pnl.ControlProjection( + function=pnl.Linear, + control_signal_params={ + pnl.ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3) + })), + noise=0.5, + starting_point=0, + t0=0.45), + output_ports=[pnl.DECISION_VARIABLE, + pnl.RESPONSE_TIME, + pnl.PROBABILITY_UPPER_THRESHOLD], + name='Decision') comp = pnl.Composition(name="evc", retain_old_simulation_data=True) # add the controller to the Composition before adding the relevant Mechanisms comp.add_controller(controller=pnl.OptimizationControlMechanism( - agent_rep=comp, - state_features=[reward.input_port, Input.input_port], - state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), - objective_mechanism=pnl.ObjectiveMechanism( - function=pnl.LinearCombination(operation=pnl.PRODUCT), - monitor=[reward, - Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], - (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), - function=pnl.GridSearch(), - control_signals=[{control_spec: ("drift_rate", Decision), - ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {control_spec: ("threshold", Decision), - ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) + agent_rep=comp, + state_features=[reward.input_port, Input.input_port], + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), + objective_mechanism=pnl.ObjectiveMechanism( + function=pnl.LinearCombination(operation=pnl.PRODUCT), + monitor=[reward, + Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], + (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), + function=pnl.GridSearch(), + control_signals=[{control_spec: ("drift_rate", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, + {control_spec: ("threshold", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) ) assert comp._controller_initialization_status == pnl.ContextFlags.DEFERRED_INIT @@ -315,7 +315,7 @@ def test_partial_deferred_init(self, state_features_option): ]) ) assert ocomp.controller.state_features == {initial_node_a: initial_node_a.input_port, - 'deferred DEFERRED':deferred_node.input_port} + 'EXPECTED INPUT NODE 1 OF ocomp':deferred_node.input_port} if state_features_option in {'list', 'shadow_inputs_dict'}: # expected_text = 'The number of \'state_features\' specified for Controller (2) is more than the ' \ @@ -849,15 +849,6 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ('full_list_spec', None, None, None), ('list_spec_with_none', None, None, None), ('input_dict_spec', None, None, None), - ('input_dict_spec_short', None, None, None), - ('set_spec', None, None, None), - ('set_spec_short', None, None, None), - ('automatic_assignment', None, None, None), - ('shadow_inputs_dict_spec', None, None, None), - ('shadow_inputs_dict_spec_w_none', None, None, None), - ('misplaced_shadow', messages[1], None, pnl.CompositionError), - ('ext_shadow', messages[2], None, pnl.OptimizationControlMechanismError), - ('ext_output_port', messages[3], None, pnl.OptimizationControlMechanismError), ('input_format_wrong_shape', messages[4], None, pnl.OptimizationControlMechanismError), ('too_many_inputs_warning', messages[5], None, UserWarning), ('too_many_w_node_not_in_composition_warning', messages[6], None, UserWarning), @@ -897,21 +888,19 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ocomp.add_linear_processing_pathway([ob,oc]) state_features_dict = { + # Legal state_features specifications 'partial_legal_list_spec': [oa.output_port], 'full_list_spec': [ia.input_port, oa.output_port, [3,1,2]], 'list_spec_with_none': [ia.input_port, None, [3,1,2]], 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK 'input_dict_spec_short': {ob:ob.output_port, oa:oc.input_port}, # Note: missing oa spec and out of order - # 'input_dict_spec': {oa:oc.input_port, ia:ia, ob:ob.output_port}, # <- ia is in nested Comp doesn't work 'set_spec': {ob, icomp, oa}, # Note: out of order is OK, and use of Nested comp as spec 'set_spec_short': {oa}, 'automatic_assignment': None, - 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, + 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, # <- ia & ob OK BECAUSE JUST FOR SHADOWING 'shadow_inputs_dict_spec_w_none': {pnl.SHADOW_INPUTS:[ia, None, ob]}, - # 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[icomp, oa, ob]}, <- BAD SHADOW SPEC - # 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, oc]}, <- OK BECAUSE IT IS JUST FOR SHADOWING - # 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:{ia, oa, ob}}, + # Illegal state_features specifications 'misplaced_shadow':ib.input_port, 'ext_shadow':ext.input_port, @@ -921,7 +910,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'too_many_inputs_error': [ia.input_port, oa.output_port, ob.output_port, oc.output_port], 'too_many_w_node_not_in_composition_warning': [ia, oa, ob, ext], 'bad_dict_spec_warning': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node - 'bad_dict_spec_error': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node + 'bad_dict_spec_error': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # ia & oc are not *ocomp* INPUT Nodes 'bad_set_spec_warning': {ob, ia}, # elicits short spec warning 'bad_set_spec_error': {ob, ia}, # elicits INPUT Node warning (for ia) 'comp_in_list_spec':[icomp, oa.output_port, [3,1,2]], # FIX: REMOVE ONCE TUPLE FORMAT SUPPORTED @@ -1002,7 +991,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {icomp: ia.input_port, oa: oa.input_port, ob: ob.input_port} + assert ocm.state_features == {ia: ia.input_port, oa: oa.input_port, ob: ob.input_port} assert all(np.allclose(expected,actual) for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) @@ -3157,18 +3146,26 @@ def test_input_CIM_assignment(self, comp_mode): # -7 ((5*-1)+(-2*1)) assert np.allclose(results, [[7]]) + state_features_arg = [ + # 'nested_partial', # <- Specify only one of two INPUT Nodes of nested comp + # 'nested_full', # <- Specify both of two INPUT Nodes of nested comp + 'automatic', # <- Automaticaly asign state_features + 'bad' # <- Specify Mechanism not in agent_rep + ] @pytest.mark.control @pytest.mark.composition @pytest.mark.parametrize('nested_agent_rep', [(False, 'OUTER COMP'),(True, 'MIDDLE COMP')], ids=['unnested','nested']) - @pytest.mark.parametrize('state_features_arg', - ['good','bad'], - ids=['good_state_feat', 'bad_state_feat']) + @pytest.mark.parametrize('state_features_arg', state_features_arg, + ids= [f"state_feature-{x}" for x in state_features_arg] + ) def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_arg): + """Also tests state_features for comp nested within nested_agent_rep""" - I = pnl.ProcessingMechanism(name='I') - icomp = pnl.Composition(nodes=I, name='INNER COMP') + I1 = pnl.ProcessingMechanism(name='I1') + I2 = pnl.ProcessingMechanism(name='I2') + icomp = pnl.Composition(nodes=[I1,I2], name='INNER COMP') A = pnl.ProcessingMechanism(name='A') B = pnl.ProcessingMechanism(name='B') C = pnl.ProcessingMechanism(name='C', size=3) @@ -3186,7 +3183,17 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ agent_rep = None error_text = '"\'OCM\' has \'state_features\' specified ([\'D[OutputPort-0]\']) that are ' \ 'missing from \'OUTER COMP\' and any Compositions nested within it."' - state_features = D.output_port if state_features_arg is 'bad' else None + # state_features = D.output_port if state_features_arg is 'bad' else None + if state_features_arg == 'nested_partial': + state_features = [A] + elif state_features_arg == 'nested_full': + state_features = [A, I1, I2] + elif state_features_arg == 'automatic': + state_features = None + elif state_features_arg == 'bad': + state_features = D.output_port + else: + assert False, "Bad state_features_arg in test." ocm = pnl.OptimizationControlMechanism(name='OCM', agent_rep=agent_rep, @@ -3194,7 +3201,7 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), allow_probes=True, function=pnl.GridSearch(), - control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I1), allocation_samples=[10, 20, 30])) if state_features_arg == 'bad': with pytest.raises(pnl.OptimizationControlMechanismError) as error: @@ -3204,7 +3211,7 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ else: ocomp.add_controller(ocm) ocomp.run() - + assert ocm.state_features == {A:A.input_port, I1:I1.input_port, I2:I2.input_port} class TestSampleIterator: From 038422f51d920e9e48c52d9f766efafa374c4be0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Mar 2022 06:45:03 +0000 Subject: [PATCH 159/285] github-actions(deps): bump actions/setup-python from 2.3.2 to 3 (#2330) --- .github/workflows/pnl-ci-docs.yml | 2 +- .github/workflows/pnl-ci.yml | 2 +- .github/workflows/test-release.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pnl-ci-docs.yml b/.github/workflows/pnl-ci-docs.yml index 97da06f2ef1..a08ee2e3f1b 100644 --- a/.github/workflows/pnl-ci-docs.yml +++ b/.github/workflows/pnl-ci-docs.yml @@ -61,7 +61,7 @@ jobs: branch: master - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.python-architecture }} diff --git a/.github/workflows/pnl-ci.yml b/.github/workflows/pnl-ci.yml index 904c1df10e0..f9da708d2f0 100644 --- a/.github/workflows/pnl-ci.yml +++ b/.github/workflows/pnl-ci.yml @@ -46,7 +46,7 @@ jobs: fetch-depth: 10 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.python-architecture }} diff --git a/.github/workflows/test-release.yml b/.github/workflows/test-release.yml index 653b0b99bb0..b3f49398edc 100644 --- a/.github/workflows/test-release.yml +++ b/.github/workflows/test-release.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} @@ -84,7 +84,7 @@ jobs: path: dist/ - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} From 14650e1aafce77ef64c1ce42652dc62ca705a062 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Mar 2022 16:06:23 +0000 Subject: [PATCH 160/285] github-actions(deps): bump actions/checkout from 2.4.0 to 3 (#2331) --- .github/workflows/pnl-ci-docs.yml | 6 +++--- .github/workflows/pnl-ci.yml | 2 +- .github/workflows/test-release.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pnl-ci-docs.yml b/.github/workflows/pnl-ci-docs.yml index a08ee2e3f1b..11a38aba68a 100644 --- a/.github/workflows/pnl-ci-docs.yml +++ b/.github/workflows/pnl-ci-docs.yml @@ -40,14 +40,14 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3 if: ${{ matrix.pnl-version == 'head' }} with: fetch-depth: 10 ref: ${{ github.ref }} - name: Checkout pull base - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3 if: ${{ matrix.pnl-version == 'base' }} with: fetch-depth: 10 @@ -142,7 +142,7 @@ jobs: steps: - name: Checkout docs - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3 with: ref: gh-pages diff --git a/.github/workflows/pnl-ci.yml b/.github/workflows/pnl-ci.yml index f9da708d2f0..37adf710d56 100644 --- a/.github/workflows/pnl-ci.yml +++ b/.github/workflows/pnl-ci.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3 with: fetch-depth: 10 diff --git a/.github/workflows/test-release.yml b/.github/workflows/test-release.yml index b3f49398edc..e54d315263a 100644 --- a/.github/workflows/test-release.yml +++ b/.github/workflows/test-release.yml @@ -18,7 +18,7 @@ jobs: wheel: ${{ steps.create_dist.outputs.wheel }} steps: - name: Checkout sources - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 @@ -113,7 +113,7 @@ jobs: run: pip install dist/${{ needs.create-python-dist.outputs.sdist }}[dev] - name: Get tests from the repository - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3 - name: Run tests shell: bash From ece6d69329ce3ea689331ceb7969a8b3d7c865b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Mar 2022 02:42:52 +0000 Subject: [PATCH 161/285] requirements: update networkx requirement from <2.6 to <2.8 (#2332) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 816b11ee73c..6c8c86f089c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ grpcio<1.43.0 grpcio-tools<1.43.0 llvmlite<0.39 matplotlib<3.5.2 -networkx<2.6 +networkx<2.8 numpy<1.21.4, >=1.17.0 pillow<9.1.0 pint<0.18 From 10e32de40889b59d556fa4eae31f1da7a33be6dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Mar 2022 16:00:51 +0000 Subject: [PATCH 162/285] github-actions(deps): bump actions/upload-artifact from 2.3.1 to 3 (#2333) --- .github/workflows/pnl-ci-docs.yml | 4 ++-- .github/workflows/pnl-ci.yml | 4 ++-- .github/workflows/test-release.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pnl-ci-docs.yml b/.github/workflows/pnl-ci-docs.yml index 11a38aba68a..eabd0b8d508 100644 --- a/.github/workflows/pnl-ci-docs.yml +++ b/.github/workflows/pnl-ci-docs.yml @@ -103,7 +103,7 @@ jobs: run: git tag -d 'v999.999.999.999' - name: Upload Documentation - uses: actions/upload-artifact@v2.3.1 + uses: actions/upload-artifact@v3 with: name: Documentation-${{matrix.pnl-version}}-${{ matrix.os }}-${{ matrix.python-version }}-${{ matrix.python-architecture }} retention-days: 1 @@ -115,7 +115,7 @@ jobs: - name: Upload PR number for other workflows if: ${{ github.event_name == 'pull_request' }} - uses: actions/upload-artifact@v2.3.1 + uses: actions/upload-artifact@v3 with: name: pr_number path: ./pr_number.txt diff --git a/.github/workflows/pnl-ci.yml b/.github/workflows/pnl-ci.yml index 37adf710d56..5794cd75646 100644 --- a/.github/workflows/pnl-ci.yml +++ b/.github/workflows/pnl-ci.yml @@ -85,7 +85,7 @@ jobs: run: pytest --junit-xml=tests_out.xml --verbosity=0 -n auto ${{ matrix.extra-args }} - name: Upload test results - uses: actions/upload-artifact@v2.3.1 + uses: actions/upload-artifact@v3 with: name: test-results-${{ matrix.os }}-${{ matrix.python-version }}-${{ matrix.python-architecture }}-${{ matrix.extra-args }} path: tests_out.xml @@ -111,7 +111,7 @@ jobs: python setup.py sdist bdist_wheel - name: Upload dist packages - uses: actions/upload-artifact@v2.3.1 + uses: actions/upload-artifact@v3 with: name: dist-${{ matrix.os }}-${{ matrix.python-version }}-${{ matrix.python-architecture }} path: dist/ diff --git a/.github/workflows/test-release.yml b/.github/workflows/test-release.yml index e54d315263a..32b6467d85e 100644 --- a/.github/workflows/test-release.yml +++ b/.github/workflows/test-release.yml @@ -38,7 +38,7 @@ jobs: echo ::set-output name=wheel::$(ls *.whl) - name: Upload Python dist files - uses: actions/upload-artifact@v2.3.1 + uses: actions/upload-artifact@v3 with: name: Python-dist-files path: dist/ @@ -126,7 +126,7 @@ jobs: pytest --junit-xml=tests_out.xml --verbosity=0 -n auto tests - name: Upload test results - uses: actions/upload-artifact@v2.3.1 + uses: actions/upload-artifact@v3 with: name: test-results-${{ matrix.os }}-${{ matrix.python-version }} path: tests_out.xml From 264e02eab71d71023abe43c43c95f35039a22f6a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 19 Nov 2021 03:15:54 -0500 Subject: [PATCH 163/285] conditions: implement Threshold --- psyneulink/core/llvm/codegen.py | 11 ++-- psyneulink/core/llvm/helpers.py | 42 +++++++++++-- psyneulink/core/scheduling/__init__.py | 6 +- psyneulink/core/scheduling/condition.py | 82 ++++++++++++++++++++++++- tests/documentation/test_module_docs.py | 5 +- tests/scheduling/test_condition.py | 68 ++++++++++++++++++++ 6 files changed, 201 insertions(+), 13 deletions(-) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index ca5d8d38818..8bbac290173 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -782,7 +782,7 @@ def gen_composition_exec(ctx, composition, *, tags:frozenset): continue reinit_cond = cond_gen.generate_sched_condition( - builder, when, cond, node, is_finished_callbacks, num_exec_locs) + builder, when, cond, node, is_finished_callbacks, num_exec_locs, nodes_states) with builder.if_then(reinit_cond): node_w = ctx.get_node_wrapper(composition, node) node_reinit_f = ctx.import_llvm_function(node_w, tags=node_tags.union({"reset"})) @@ -816,7 +816,7 @@ def gen_composition_exec(ctx, composition, *, tags:frozenset): trial_term_cond = cond_gen.generate_sched_condition( builder, composition.termination_processing[TimeScale.TRIAL], - cond, None, is_finished_callbacks, num_exec_locs) + cond, None, is_finished_callbacks, num_exec_locs, nodes_states) trial_cond = builder.not_(trial_term_cond, name="not_trial_term_cond") loop_body = builder.append_basic_block(name="scheduling_loop_body") @@ -835,7 +835,7 @@ def gen_composition_exec(ctx, composition, *, tags:frozenset): name="run_cond_ptr_" + node.name) node_cond = cond_gen.generate_sched_condition( builder, composition._get_processing_condition_set(node), - cond, node, is_finished_callbacks, num_exec_locs) + cond, node, is_finished_callbacks, num_exec_locs, nodes_states) ran = cond_gen.generate_ran_this_pass(builder, cond, node) node_cond = builder.and_(node_cond, builder.not_(ran), name="run_cond_" + node.name) @@ -947,6 +947,9 @@ def gen_composition_run(ctx, composition, *, tags:frozenset): a.attributes.add('noalias') state, params, data, data_in, data_out, trials_ptr, inputs_ptr = llvm_func.args + + nodes_states = helpers.get_state_ptr(builder, composition, state, "nodes") + # simulation does not care about the output # it extracts results of the controller objective mechanism if simulation: @@ -994,7 +997,7 @@ def gen_composition_run(ctx, composition, *, tags:frozenset): run_term_cond = cond_gen.generate_sched_condition( builder, composition.termination_processing[TimeScale.RUN], - cond, None, None, None) + cond, None, None, None, nodes_states) run_cond = builder.not_(run_term_cond, name="not_run_term_cond") # Iter cond diff --git a/psyneulink/core/llvm/helpers.py b/psyneulink/core/llvm/helpers.py index 13f2feaafcf..2d7f50779fe 100644 --- a/psyneulink/core/llvm/helpers.py +++ b/psyneulink/core/llvm/helpers.py @@ -15,7 +15,7 @@ from .debug import debug_env from psyneulink.core.scheduling.condition import All, AllHaveRun, Always, Any, AtPass, AtTrial, BeforeNCalls, AtNCalls, AfterNCalls, \ - EveryNCalls, Never, Not, WhenFinished, WhenFinishedAny, WhenFinishedAll + EveryNCalls, Never, Not, WhenFinished, WhenFinishedAny, WhenFinishedAll, Threshold from psyneulink.core.scheduling.time import TimeScale @@ -570,8 +570,10 @@ def generate_ran_this_trial(self, builder, cond_ptr, node): return builder.icmp_signed("==", node_trial, global_trial) + # TODO: replace num_exec_locs use with equivalent from nodes_states def generate_sched_condition(self, builder, condition, cond_ptr, node, - is_finished_callbacks, num_exec_locs): + is_finished_callbacks, num_exec_locs, + nodes_states): if isinstance(condition, Always): @@ -581,13 +583,13 @@ def generate_sched_condition(self, builder, condition, cond_ptr, node, return self.ctx.bool_ty(0) elif isinstance(condition, Not): - orig_condition = self.generate_sched_condition(builder, condition.condition, cond_ptr, node, is_finished_callbacks, num_exec_locs) + orig_condition = self.generate_sched_condition(builder, condition.condition, cond_ptr, node, is_finished_callbacks, num_exec_locs, nodes_states) return builder.not_(orig_condition) elif isinstance(condition, All): agg_cond = self.ctx.bool_ty(1) for cond in condition.args: - cond_res = self.generate_sched_condition(builder, cond, cond_ptr, node, is_finished_callbacks, num_exec_locs) + cond_res = self.generate_sched_condition(builder, cond, cond_ptr, node, is_finished_callbacks, num_exec_locs, nodes_states) agg_cond = builder.and_(agg_cond, cond_res) return agg_cond @@ -611,7 +613,7 @@ def generate_sched_condition(self, builder, condition, cond_ptr, node, elif isinstance(condition, Any): agg_cond = self.ctx.bool_ty(0) for cond in condition.args: - cond_res = self.generate_sched_condition(builder, cond, cond_ptr, node, is_finished_callbacks, num_exec_locs) + cond_res = self.generate_sched_condition(builder, cond, cond_ptr, node, is_finished_callbacks, num_exec_locs, nodes_states) agg_cond = builder.or_(agg_cond, cond_res) return agg_cond @@ -699,4 +701,34 @@ def generate_sched_condition(self, builder, condition, cond_ptr, node, return run_cond + elif isinstance(condition, Threshold): + target = condition.dependency + param = condition.parameter + threshold = condition.threshold + comparator = condition.comparator + indices = condition.indices + + assert param in target.llvm_state_ids, ( + f"Threshold for {target} only supports items in llvm_state_ids" + f" ({target.llvm_state_ids})" + ) + + node_idx = self.composition._get_node_index(target) + node_state = builder.gep(nodes_states, [self.ctx.int32_ty(0), self.ctx.int32_ty(node_idx)]) + param_ptr = get_state_ptr(builder, target, node_state, param) + + if isinstance(param_ptr.type.pointee, ir.ArrayType): + if indices is None: + indices = [0, 0] + elif isinstance(indices, TimeScale): + indices = [indices.value] + + indices = [self.ctx.int32_ty(x) for x in [0] + list(indices)] + param_ptr = builder.gep(param_ptr, indices) + + val = builder.load(param_ptr) + val = convert_type(builder, val, ir.DoubleType()) + + return builder.fcmp_ordered(comparator, val, val.type(threshold)) + assert False, "Unsupported scheduling condition: {}".format(condition) diff --git a/psyneulink/core/scheduling/__init__.py b/psyneulink/core/scheduling/__init__.py index fc9dcf1fed3..34b37a0528e 100644 --- a/psyneulink/core/scheduling/__init__.py +++ b/psyneulink/core/scheduling/__init__.py @@ -58,7 +58,11 @@ cls = cls_name if cls.__doc__ is None: - cls.__doc__ = f'{getattr(ext_module, cls_name).__doc__}' + try: + cls.__doc__ = f'{getattr(ext_module, cls_name).__doc__}' + except AttributeError: + # PNL-exclusive object + continue cls.__doc__ = re.sub(pattern, repl, cls.__doc__, flags=re.MULTILINE | re.DOTALL) diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index 58015b7f589..46e657a23c9 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -8,6 +8,8 @@ # ********************************************* Condition ************************************************************** +import collections +import copy import functools import inspect @@ -16,10 +18,11 @@ from psyneulink.core.globals.context import handle_external_context from psyneulink.core.globals.json import JSONDumpable -from psyneulink.core.globals.keywords import MODEL_SPEC_ID_TYPE +from psyneulink.core.globals.keywords import MODEL_SPEC_ID_TYPE, comparison_operators from psyneulink.core.globals.parameters import parse_context -__all__ = graph_scheduler.condition.__all__ +__all__ = copy.copy(graph_scheduler.condition.__all__) +__all__.extend(['Threshold']) def _create_as_pnl_condition(condition): @@ -152,3 +155,78 @@ def _dict_summary(self): ) ] } + + +class Threshold(graph_scheduler.condition._DependencyValidation, Condition): + """Threshold + + Attributes: + + dependency + the node on which the Condition depends + + parameter + the name of the parameter of **dependency** whose value is + to be compared to **threshold** + + threshold + the fixed value compared to the value of the **parameter** + + comparator + the string comparison operator determining the direction or + type of comparison of the value of the **parameter** + relative to **threshold** + + indices + if specified, a series of indices that reach the desired + number given an iterable value for **parameter** + + Satisfied when: + + The comparison between the value of the **parameter** and + **threshold** using **comparator** is true + + Notes: + + The comparison must be done with scalars. If the value of + **parameter** contains more than one item, **indices** must be + specified. + """ + + def __init__(self, dependency, parameter, threshold, comparator, indices=None): + if comparator not in comparison_operators: + raise graph_scheduler.ConditionError( + f'Operator must be one of {list(comparison_operators.keys())}' + ) + + if parameter not in dependency.parameters: + raise graph_scheduler.ConditionError( + f'{dependency} has no {parameter} parameter' + ) + + if ( + indices is not None + and not isinstance(indices, graph_scheduler.TimeScale) + and not isinstance(indices, collections.abc.Iterable) + ): + indices = [indices] + + def func(threshold, comparator, indices, execution_id): + param_value = getattr(self.dependency.parameters, self.parameter).get(execution_id) + + if isinstance(indices, graph_scheduler.TimeScale): + param_value = param_value._get_by_time_scale(indices) + elif indices is not None: + for i in indices: + param_value = param_value[i] + + return comparison_operators[comparator](float(param_value), threshold) + + super().__init__( + func, + dependency=dependency, + parameter=parameter, + threshold=threshold, + comparator=comparator, + indices=indices, + ) diff --git a/tests/documentation/test_module_docs.py b/tests/documentation/test_module_docs.py index 0a951c3fcbf..ecc18564bf0 100644 --- a/tests/documentation/test_module_docs.py +++ b/tests/documentation/test_module_docs.py @@ -72,7 +72,10 @@ def test_scheduler_substitutions(mod): for pattern, repl in pnl.core.scheduling._global_doc_subs: for cls_name in mod.__all__: cls = getattr(mod, cls_name) - ext_cls = getattr(graph_scheduler, cls_name) + try: + ext_cls = getattr(graph_scheduler, cls_name) + except AttributeError: + continue # global replacements may not happen in every docstring assert re.sub(r'\\\d', '', repl) in cls.__doc__ or not re.match(pattern, ext_cls.__doc__) diff --git a/tests/scheduling/test_condition.py b/tests/scheduling/test_condition.py index 9933f601b4a..d1b7f468af1 100644 --- a/tests/scheduling/test_condition.py +++ b/tests/scheduling/test_condition.py @@ -1,5 +1,7 @@ import logging +import numpy as np +import psyneulink as pnl import pytest from psyneulink import _unit_registry from psyneulink.core.components.functions.nonstateful.transferfunctions import Linear @@ -690,6 +692,72 @@ def test_AllHaveRun_2(self): ] assert output == pytest.helpers.setify_expected_output(expected_output) + @pytest.mark.parametrize( + 'parameter, indices, default_variable, integration_rate, expected_results', + [ + ('value', None, None, 1, [[[10]]]), + ('value', (0, 0), [[0, 0]], [1, 2], [[[10, 20]]]), + ('value', (0, 1), [[0, 0]], [1, 2], [[[5, 10]]]), + ('num_executions', TimeScale.TRIAL, None, 1, [[[10]]]), + ('execution_count', None, None, 1, [[[10]]]), + ] + ) + @pytest.mark.parametrize('threshold', [10, 10.0]) + def test_Threshold_parameters( + self, parameter, indices, default_variable, integration_rate, expected_results, threshold, comp_mode + ): + if comp_mode is pnl.ExecutionMode.LLVM: + pytest.skip('ExecutionMode.LLVM does not support Parameter access in conditions') + + A = TransferMechanism( + default_variable=default_variable, + integrator_mode=True, + integrator_function=pnl.SimpleIntegrator, + integration_rate=integration_rate, + ) + comp = Composition(pathways=[A]) + + comp.termination_processing = { + pnl.TimeScale.TRIAL: pnl.Threshold(A, parameter, threshold, '>=', indices=indices) + } + + comp.run(inputs={A: np.ones(A.defaults.variable.shape)}, execution_mode=comp_mode) + + np.testing.assert_array_equal(comp.results, expected_results) + + @pytest.mark.parametrize( + 'comparator, increment, threshold, expected_results', + [ + ('>', 1, 5, [[[6]]]), + ('>=', 1, 5, [[[5]]]), + ('<', -1, -5, [[[-6]]]), + ('<=', -1, -5, [[[-5]]]), + ('==', 1, 5, [[[5]]]), + ('!=', 1, 0, [[[1]]]), + ('!=', -1, 0, [[[-1]]]), + ] + ) + def test_Threshold_comparators( + self, comparator, increment, threshold, expected_results, comp_mode + ): + if comp_mode is pnl.ExecutionMode.LLVM: + pytest.skip('ExecutionMode.LLVM does not support Parameter access in conditions') + + A = TransferMechanism( + integrator_mode=True, + integrator_function=pnl.AccumulatorIntegrator(rate=1, increment=increment), + ) + comp = Composition(pathways=[A]) + + comp.termination_processing = { + pnl.TimeScale.TRIAL: pnl.Threshold(A, 'value', threshold, comparator) + } + + comp.run(inputs={A: np.ones(A.defaults.variable.shape)}, execution_mode=comp_mode) + + np.testing.assert_array_equal(comp.results, expected_results) + + class TestWhenFinished: @classmethod From b6b82dfa4857cdbe664c227d0353a636079e5243 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 25 Jan 2022 21:09:50 -0500 Subject: [PATCH 164/285] Threshold: implement tolerances --- psyneulink/core/llvm/helpers.py | 8 +++++- psyneulink/core/scheduling/condition.py | 31 ++++++++++++++++++---- tests/scheduling/test_condition.py | 34 +++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/psyneulink/core/llvm/helpers.py b/psyneulink/core/llvm/helpers.py index 2d7f50779fe..606b0f6a1b1 100644 --- a/psyneulink/core/llvm/helpers.py +++ b/psyneulink/core/llvm/helpers.py @@ -728,7 +728,13 @@ def generate_sched_condition(self, builder, condition, cond_ptr, node, val = builder.load(param_ptr) val = convert_type(builder, val, ir.DoubleType()) + threshold = val.type(threshold) - return builder.fcmp_ordered(comparator, val, val.type(threshold)) + if comparator == '==': + return is_close(self.ctx, builder, val, threshold, condition.rtol, condition.atol) + elif comparator == '!=': + return builder.not_(is_close(self.ctx, builder, val, threshold, condition.rtol, condition.atol)) + else: + return builder.fcmp_ordered(comparator, val, threshold) assert False, "Unsupported scheduling condition: {}".format(condition) diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index 46e657a23c9..b39e9ca53c0 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -12,10 +12,11 @@ import copy import functools import inspect +import warnings import dill import graph_scheduler - +import numpy as np from psyneulink.core.globals.context import handle_external_context from psyneulink.core.globals.json import JSONDumpable from psyneulink.core.globals.keywords import MODEL_SPEC_ID_TYPE, comparison_operators @@ -181,10 +182,18 @@ class Threshold(graph_scheduler.condition._DependencyValidation, Condition): if specified, a series of indices that reach the desired number given an iterable value for **parameter** + atol + absolute tolerance for the comparison + + rtol + relative tolerance (to **threshold**) for the comparison + Satisfied when: The comparison between the value of the **parameter** and - **threshold** using **comparator** is true + **threshold** using **comparator** is true. If **comparator** is + an equality (==, !=), the comparison will be considered equal + within tolerances **atol** and **rtol**. Notes: @@ -193,7 +202,7 @@ class Threshold(graph_scheduler.condition._DependencyValidation, Condition): specified. """ - def __init__(self, dependency, parameter, threshold, comparator, indices=None): + def __init__(self, dependency, parameter, threshold, comparator, indices=None, atol=0, rtol=0): if comparator not in comparison_operators: raise graph_scheduler.ConditionError( f'Operator must be one of {list(comparison_operators.keys())}' @@ -204,6 +213,9 @@ def __init__(self, dependency, parameter, threshold, comparator, indices=None): f'{dependency} has no {parameter} parameter' ) + if (atol != 0 or rtol != 0) and comparator in {'<', '<=', '>', '>='}: + warnings.warn('Tolerances for inequality comparators are ignored') + if ( indices is not None and not isinstance(indices, graph_scheduler.TimeScale) @@ -211,7 +223,7 @@ def __init__(self, dependency, parameter, threshold, comparator, indices=None): ): indices = [indices] - def func(threshold, comparator, indices, execution_id): + def func(threshold, comparator, indices, atol, rtol, execution_id): param_value = getattr(self.dependency.parameters, self.parameter).get(execution_id) if isinstance(indices, graph_scheduler.TimeScale): @@ -220,7 +232,14 @@ def func(threshold, comparator, indices, execution_id): for i in indices: param_value = param_value[i] - return comparison_operators[comparator](float(param_value), threshold) + param_value = float(param_value) + + if comparator == '==': + return np.isclose(param_value, threshold, atol=atol, rtol=rtol) + elif comparator == '!=': + return not np.isclose(param_value, threshold, atol=atol, rtol=rtol) + else: + return comparison_operators[comparator](param_value, threshold) super().__init__( func, @@ -229,4 +248,6 @@ def func(threshold, comparator, indices, execution_id): threshold=threshold, comparator=comparator, indices=indices, + atol=atol, + rtol=rtol, ) diff --git a/tests/scheduling/test_condition.py b/tests/scheduling/test_condition.py index d1b7f468af1..111829a8d4f 100644 --- a/tests/scheduling/test_condition.py +++ b/tests/scheduling/test_condition.py @@ -757,6 +757,40 @@ def test_Threshold_comparators( np.testing.assert_array_equal(comp.results, expected_results) + @pytest.mark.parametrize( + 'comparator, increment, threshold, atol, rtol, expected_results', + [ + ('==', 1, 10, 1, 0.1, [[[8]]]), + ('==', 1, 10, 1, 0, [[[9]]]), + ('==', 1, 10, 0, 0.1, [[[9]]]), + ('!=', 1, 2, 1, 0.5, [[[5]]]), + ('!=', 1, 1, 1, 0, [[[3]]]), + ('!=', 1, 1, 0, 1, [[[3]]]), + ('!=', -1, -2, 1, 0.5, [[[-5]]]), + ('!=', -1, -1, 1, 0, [[[-3]]]), + ('!=', -1, -1, 0, 1, [[[-3]]]), + ] + ) + def test_Threshold_tolerances( + self, comparator, increment, threshold, atol, rtol, expected_results, comp_mode + ): + if comp_mode is pnl.ExecutionMode.LLVM: + pytest.skip('ExecutionMode.LLVM does not support Parameter access in conditions') + + A = TransferMechanism( + integrator_mode=True, + integrator_function=pnl.AccumulatorIntegrator(rate=1, increment=increment), + ) + comp = Composition(pathways=[A]) + + comp.termination_processing = { + pnl.TimeScale.TRIAL: pnl.Threshold(A, 'value', threshold, comparator, atol=atol, rtol=rtol) + } + + comp.run(inputs={A: np.ones(A.defaults.variable.shape)}, execution_mode=comp_mode) + + np.testing.assert_array_equal(comp.results, expected_results) + class TestWhenFinished: From 7639454705f7dfcd33a9bddcdcb7261c4fcef4ac Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 1 Feb 2022 22:33:12 -0500 Subject: [PATCH 165/285] tests, models: replace custom threshold conditions with Threshold --- psyneulink/library/models/Cohen_Huston1994.py | 14 ++--- .../models/Cohen_Huston1994_horse_race.py | 52 ++++++++++--------- .../library/models/Kalanthroff_PCTC_2018.py | 17 ++---- tests/composition/test_models.py | 24 ++------- tests/log/test_log.py | 27 ++-------- tests/log/test_rpc.py | 18 +------ 6 files changed, 45 insertions(+), 107 deletions(-) diff --git a/psyneulink/library/models/Cohen_Huston1994.py b/psyneulink/library/models/Cohen_Huston1994.py index 7655230cdbd..ff455921d40 100644 --- a/psyneulink/library/models/Cohen_Huston1994.py +++ b/psyneulink/library/models/Cohen_Huston1994.py @@ -272,17 +272,11 @@ # Create threshold function ------------------------------------------------------------------------------------------- - -def pass_threshold(response_layer, thresh): - results1 = response_layer.get_output_values(Bidirectional_Stroop)[0][0] # red response - results2 = response_layer.get_output_values(Bidirectional_Stroop)[0][1] # green response - if results1 >= thresh or results2 >= thresh: - return True - return False - - terminate_trial = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold, response_layer, threshold) + pnl.TimeScale.TRIAL: pnl.Or( + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 0)), + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 1)), + ) } # Create test trials function ----------------------------------------------------------------------------------------- diff --git a/psyneulink/library/models/Cohen_Huston1994_horse_race.py b/psyneulink/library/models/Cohen_Huston1994_horse_race.py index 54774e9cfdd..39365f0c08d 100644 --- a/psyneulink/library/models/Cohen_Huston1994_horse_race.py +++ b/psyneulink/library/models/Cohen_Huston1994_horse_race.py @@ -185,43 +185,45 @@ # Bidirectional_Stroop.show_graph(show_dimensions=pnl.ALL)#,show_mechanism_structure=pnl.VALUES) # Uncomment to show graph of the system -# Create threshold function ------------------------------------------------------------------------------------------- -# context is automatically passed into Conditions, and references the execution context in which they are being run, -# which in this case is simply the Bidirectional_Stroop system -def pass_threshold(response_layer, thresh, context): - results1 = response_layer.get_output_values(context)[0][0] #red response - results2 = response_layer.get_output_values(context)[0][1] #green response - if results1 >= thresh or results2 >= thresh: - return True - return False - -# 2nd threshold function -def pass_threshold2(response_layer, thresh, terminate, context): - results1 = response_layer.get_output_values(context)[0][0] #red response - results2 = response_layer.get_output_values(context)[0][1] #green response - length = response_layer.log.nparray_dictionary()[context.execution_id]['value'].shape[0] - if results1 >= thresh or results2 >= thresh: - return True - if length ==terminate: - return True - return False + +def terminate_num_executions(length, context): + return response_layer.log.nparray_dictionary()[context.execution_id]['value'].shape[0] == length # Create different terminate trial conditions -------------------------------------------------------------------------- terminate_trial = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold, response_layer, threshold) + pnl.TimeScale.TRIAL: pnl.Or( + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 0)), + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 1)), + ) } terminate_trial2 = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold2, response_layer, threshold, terminate2) + pnl.TimeScale.TRIAL: pnl.Or( + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 0)), + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 1)), + pnl.While(terminate_num_executions, terminate2), + ) } terminate_trial3 = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold2, response_layer, threshold, terminate3) + pnl.TimeScale.TRIAL: pnl.Or( + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 0)), + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 1)), + pnl.While(terminate_num_executions, terminate3), + ) } terminate_trial4 = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold2, response_layer, threshold, terminate4) + pnl.TimeScale.TRIAL: pnl.Or( + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 0)), + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 1)), + pnl.While(terminate_num_executions, terminate4), + ) } terminate_trial5 = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold2, response_layer, threshold, terminate5) + pnl.TimeScale.TRIAL: pnl.Or( + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 0)), + pnl.Threshold(response_layer, 'value', threshold, '>=', (0, 1)), + pnl.While(terminate_num_executions, terminate5), + ) } terminate_list = [terminate_trial2, diff --git a/psyneulink/library/models/Kalanthroff_PCTC_2018.py b/psyneulink/library/models/Kalanthroff_PCTC_2018.py index 2c435eae76d..69269e01ea4 100644 --- a/psyneulink/library/models/Kalanthroff_PCTC_2018.py +++ b/psyneulink/library/models/Kalanthroff_PCTC_2018.py @@ -290,20 +290,13 @@ def my_conflict_function(variable): reinitialize_mechanisms_when=pnl.Never(), name='PCTC_MODEL') -# Create threshold function ------------------------------------------------------------------------------------------- - -def pass_threshold(response_layer, thresh, context): - results1 = response_layer.get_output_values(context)[0][0] # red response - results2 = response_layer.get_output_values(context)[0][1] # green response - # print(results1) - # print(results2) - if results1 >= thresh or results2 >= thresh: - return True - return False - +# Create threshold condition ------------------------------------------------------------------------------------------- terminate_trial = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold, response_layer, threshold) + pnl.TimeScale.TRIAL: pnl.Or( + pnl.Threshold(response_layer.output_ports['SPECIAL_LOGISTIC'], 'value', threshold, '>=', 0), + pnl.Threshold(response_layer.output_ports['SPECIAL_LOGISTIC'], 'value', threshold, '>=', 1), + ) } # Create test trials function ----------------------------------------------------------------------------------------- diff --git a/tests/composition/test_models.py b/tests/composition/test_models.py index 3db9c9c2092..a439db6c872 100644 --- a/tests/composition/test_models.py +++ b/tests/composition/test_models.py @@ -231,21 +231,6 @@ def trial_dict(red_color, green_color, red_word, green_word, CN, WR): } return trialdict - # CREATE THRESHOLD FUNCTION - # first value of DDM's value is DECISION_VARIABLE - # context is always passed to Condition functions and is the context - # in which the function gets called - below, during system execution - def pass_threshold(mech1, mech2, thresh, context=None): - results1 = mech1.output_ports[0].parameters.value.get(context) - results2 = mech2.output_ports[0].parameters.value.get(context) - for val in results1: - if val >= thresh: - return True - for val in results2: - if val >= thresh: - return True - return False - accumulator_threshold = 1.0 mechanisms_to_update = [colors_hidden_layer, words_hidden_layer, response_layer] @@ -272,12 +257,11 @@ def switch_to_processing_trial(mechanisms): # Turn on noise switch_noise(mechanisms, psyneulink.core.components.functions.nonstateful.distributionfunctions.NormalDist(mean=0, standard_deviation=unit_noise).function) # Execute until one of the accumulators crosses the threshold + # first value of DDM's value is DECISION_VARIABLE my_Stroop.termination_processing = { - pnl.TimeScale.TRIAL: pnl.While( - pass_threshold, - respond_red_accumulator, - respond_green_accumulator, - accumulator_threshold + pnl.TimeScale.TRIAL: pnl.Or( + pnl.Threshold(respond_red_accumulator, 'value', accumulator_threshold, '>=', (0, 0)), + pnl.Threshold(respond_green_accumulator, 'value', accumulator_threshold, '>=', (0, 0)) ) } diff --git a/tests/log/test_log.py b/tests/log/test_log.py index 9cb42a1c37f..bfe9ab7b5c1 100644 --- a/tests/log/test_log.py +++ b/tests/log/test_log.py @@ -780,15 +780,8 @@ def test_log_dictionary_with_scheduler(self): function=psyneulink.core.components.functions.nonstateful.transferfunctions.Linear(slope=6.0)) COMP = pnl.Composition(name='log_test_COMP', pathways=[T1, T2]) - def pass_threshold(mech, thresh): - results = mech.output_ports[0].parameters.value.get(COMP) - for val in results: - if abs(val) >= thresh: - return True - return False - terminate_trial = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold, T2, 5.0) + pnl.TimeScale.TRIAL: pnl.Threshold(T2, 'value', 5.0, '>=') } T1.set_log_conditions(pnl.VALUE) @@ -833,15 +826,8 @@ def test_log_array_with_scheduler(self): function=psyneulink.core.components.functions.nonstateful.transferfunctions.Linear(slope=6.0)) COMP = pnl.Composition(name='log_test_COMP', pathways=[T1, T2]) - def pass_threshold(mech, thresh): - results = mech.output_ports[0].parameters.value.get(COMP) - for val in results: - if abs(val) >= thresh: - return True - return False - terminate_trial = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold, T2, 5.0) + pnl.TimeScale.TRIAL: pnl.Threshold(T2, 'value', 5.0, '>=') } T1.set_log_conditions(pnl.VALUE) @@ -908,15 +894,8 @@ def test_log_dictionary_with_scheduler_many_time_step_increments(self): integration_rate=0.05) COMP = pnl.Composition(name='log_test_COMP', pathways=[T1]) - def pass_threshold(mech, thresh): - results = mech.output_ports[0].parameters.value.get(COMP) - for val in results: - if abs(val) >= thresh: - return True - return False - terminate_trial = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold, T1, 0.95) + pnl.TimeScale.TRIAL: pnl.Threshold(T1, 'value', 0.95, '>=') } T1.set_log_conditions(pnl.VALUE) diff --git a/tests/log/test_rpc.py b/tests/log/test_rpc.py index 213673efecb..405706cf97c 100644 --- a/tests/log/test_rpc.py +++ b/tests/log/test_rpc.py @@ -259,15 +259,8 @@ def test_log_dictionary_with_scheduler(self): con_with_rpc_pipeline = pnl.Context(rpc_pipeline=Queue(), execution_id=COMP) pipeline = con_with_rpc_pipeline.rpc_pipeline - def pass_threshold(mech, thresh): - results = mech.output_ports[0].parameters.value.get(COMP) - for val in results: - if abs(val) >= thresh: - return True - return False - terminate_trial = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold, T2, 5.0) + pnl.TimeScale.TRIAL: pnl.Threshold(T2, 'value', 5.0, '>=', (0, 0)) } T1.set_delivery_conditions(pnl.VALUE) @@ -332,15 +325,8 @@ def test_log_dictionary_with_scheduler_many_time_step_increments(self): con_with_rpc_pipeline = pnl.Context(rpc_pipeline=Queue(), execution_id=COMP) pipeline = con_with_rpc_pipeline.rpc_pipeline - def pass_threshold(mech, thresh): - results = mech.output_ports[0].parameters.value.get(COMP) - for val in results: - if abs(val) >= thresh: - return True - return False - terminate_trial = { - pnl.TimeScale.TRIAL: pnl.While(pass_threshold, T1, 0.95) + pnl.TimeScale.TRIAL: pnl.Threshold(T1, 'value', 0.95, '>=') } T1.set_delivery_conditions(pnl.VALUE) From 39adfb18aa90e6f1e817e945bd0eb949004eccc7 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 27 Feb 2022 16:44:21 -0500 Subject: [PATCH 166/285] llvm/builder_context: Rename types int_ty -> copy_ty The types determines copy size, 'int' is secondary. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/builder_context.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/psyneulink/core/llvm/builder_context.py b/psyneulink/core/llvm/builder_context.py index 01f7a8d3a09..6fa5af54287 100644 --- a/psyneulink/core/llvm/builder_context.py +++ b/psyneulink/core/llvm/builder_context.py @@ -483,8 +483,12 @@ def _gen_cuda_kernel_wrapper_module(function): tid_x_f = ir.Function(module, intrin_ty, "llvm.nvvm.read.ptx.sreg.tid.x") ntid_x_f = ir.Function(module, intrin_ty, "llvm.nvvm.read.ptx.sreg.ntid.x") ctaid_x_f = ir.Function(module, intrin_ty, "llvm.nvvm.read.ptx.sreg.ctaid.x") + + # Number of threads per block ntid = builder.call(ntid_x_f, []) + # Thread ID in block tid = builder.call(tid_x_f, []) + global_id = builder.mul(builder.call(ctaid_x_f, []), ntid) global_id = builder.add(global_id, tid) @@ -517,12 +521,13 @@ def _upload_to_shared(b, ptr, name): obj_size = b.call(obj_size_f, [ptr_dst, bool_ty(1), bool_ty(0), bool_ty(0)]) if "unaligned_copy" not in debug_env: - int_ty = ir.IntType(32) - int_ptr_ty = int_ty.as_pointer() - obj_size = builder.add(obj_size, obj_size.type((int_ty.width // 8) - 1)) - obj_size = builder.udiv(obj_size, obj_size.type(int_ty.width // 8)) - ptr_src = builder.bitcast(ptr, int_ptr_ty) - ptr_dst = builder.bitcast(shared_ptr, int_ptr_ty) + copy_ty = ir.IntType(32) + copy_ptr_ty = copy_ty.as_pointer() + copy_bytes = copy_ty.width // 8 + obj_size = builder.add(obj_size, obj_size.type(copy_bytes - 1)) + obj_size = builder.udiv(obj_size, obj_size.type(copy_bytes)) + ptr_src = builder.bitcast(ptr, copy_ptr_ty) + ptr_dst = builder.bitcast(shared_ptr, copy_ptr_ty) # copy data using as many threads as available in thread group From 689ea544fd43f0e90f6585a877dd3e7b34385ba0 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 8 Mar 2022 01:07:13 -0500 Subject: [PATCH 167/285] llvm/codegen: Add and clarify assertions Signed-off-by: Jan Vesely --- psyneulink/core/llvm/codegen.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index 8bbac290173..09284627ff4 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -616,9 +616,10 @@ def gen_node_wrapper(ctx, composition, node, *, tags:frozenset): ctx.int32_ty(parent_idx), ctx.int32_ty(output_port_idx)]) - # Get location of projection output (in mechanism's input structure + # Get location of projection output (in mechanism's input structure) rec_port = proj.receiver - assert rec_port.owner is node or rec_port.owner is node.input_CIM or rec_port.owner is node.parameter_CIM + assert not is_mech or rec_port.owner is node + assert is_mech or rec_port.owner is node.input_CIM or rec_port.owner is node.parameter_CIM indices = [0] if proj in rec_port.owner.path_afferents: rec_port_idx = rec_port.owner.input_ports.index(rec_port) @@ -655,7 +656,14 @@ def gen_node_wrapper(ctx, composition, node, *, tags:frozenset): if proj_out.type != proj_function.args[3].type: warnings.warn("Shape mismatch: Projection ({}) results does not match the receiver state({}) input: {} vs. {}".format(proj, proj.receiver, proj.defaults.value, proj.receiver.defaults.variable)) + # Check that this workaround applies only to projections to inner composition + # that are off by one dimension, but in the 'wrong' direction (we need to add one dim). + assert not is_mech + assert len(proj_function.args[3].type.pointee) == 1 + assert proj_function.args[3].type.pointee.element == proj_out.type.pointee + proj_out = builder.bitcast(proj_out, proj_function.args[3].type) + builder.call(proj_function, [proj_params, proj_state, proj_in, proj_out]) From 936e0fc1044697d284a11648c2b43dd68f63fcbb Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 8 Mar 2022 13:11:45 -0500 Subject: [PATCH 168/285] fix aggregation function --- .../nonstateful/optimizationfunctions.py | 37 +++++++++++++------ .../ports/modulatorysignals/controlsignal.py | 5 ++- .../parameterestimationcomposition.py | 2 +- tests/composition/test_control.py | 20 +++++----- tests/functions/test_integrator.py | 6 +-- .../test_projection_specifications.py | 2 +- 6 files changed, 46 insertions(+), 26 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index ef651f9d5f6..e2fc1dab86a 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -386,7 +386,7 @@ class Parameters(Function_Base.Parameters): variable = Parameter(np.array([0.0, 0.0, 0.0]), read_only=True, pnl_internal=True, constructor_argument='default_variable') objective_function = Parameter(lambda x: 0.0, stateful=False, loggable=False) - aggregation_function = Parameter(lambda x: np.mean(x, axis=x.ndim-1), stateful=False, loggable=False) + aggregation_function = Parameter(lambda x: np.mean(x, axis=1), stateful=False, loggable=False) search_function = Parameter(lambda x: x, stateful=False, loggable=False) search_termination_function = Parameter(lambda x, y, z: True, stateful=False, loggable=False) search_space = Parameter([SampleIterator([0])], stateful=False, loggable=False) @@ -650,9 +650,24 @@ def _evaluate(self, variable=None, context=None, params=None): # If aggregation_function is specified and there is a randomization dimension specified # in the control signals; use the aggregation function aggregate over the samples generated # for different randomized values of the control signal - if self.aggregation_function and self.parameters.randomization_dimension._get(context): - aggregated_values = np.atleast_1d(self.aggregation_function(all_values)) + if self.aggregation_function and \ + self.parameters.randomization_dimension._get(context) and \ + self.parameters.num_estimates._get(context) is not None: + + # Reshape all the values we encountered to group those that correspond to the same parameter values + # can be aggregated. + all_values = np.reshape(all_values, (-1, self.parameters.num_estimates._get(context))) + + # Since we are aggregating over the randomized value of the control allocation, we also need to drop the + # randomized dimension from the samples. That is, we don't want to return num_estimates samples for each + # control allocation. This line below just grabs the first one (seed == 1) for each control allocation. + all_samples = all_samples[:, all_samples[1, :] == all_samples[1, 0]] + + # If num_estimates is not None, then one of the control signals is modulating the random seed. We will + # groupby this signal and average the values to compute the estimated value. + aggregated_values = np.atleast_2d(self.aggregation_function(all_values)) returned_values = aggregated_values + else: returned_values = all_values @@ -728,8 +743,8 @@ def _sequential_evaluate(self, initial_sample, initial_value, context): self.parameters.saved_values._set(all_values, context) # Convert evaluated_samples and estimated_values to numpy arrays, stack along the last dimension - estimated_values = np.stack(estimated_values, axis=estimated_values[0].ndim) - evaluated_samples = np.stack(evaluated_samples, axis=evaluated_samples[0].ndim) + estimated_values = np.stack(estimated_values, axis=-1) + evaluated_samples = np.stack(evaluated_samples, axis=-1) # FIX: 11/3/21: ??MODIFY TO RETURN SAME AS _grid_evaluate # return current_sample, current_value, evaluated_samples, estimated_values @@ -837,7 +852,7 @@ class GradientOptimization(OptimizationFunction): :math:`\\frac{d(objective\\_function(variable))}{d(variable)}`. If the **gradient_function* argument of the constructor is not specified, then an attempt is made to use `Autograd's `_ `grad ` method to generate `gradient_function `. If that fails, - an erorr occurs. The **search_space** argument can be used to specify lower and/or upper bounds for each dimension + an error occurs. The **search_space** argument can be used to specify lower and/or upper bounds for each dimension of the sample; if the gradient causes a value of the sample to exceed a bound along a dimenson, the value of the bound is used for that dimension, unless/until the gradient shifts and causes it to return back within the bound. @@ -1461,8 +1476,8 @@ class Parameters(OptimizationFunction.Parameters): :type: ``bool`` """ grid = Parameter(None) - save_samples = Parameter(True, pnl_internal=True) - save_values = Parameter(True, pnl_internal=True) + save_samples = Parameter(False, pnl_internal=True) + save_values = Parameter(False, pnl_internal=True) random_state = Parameter(None, loggable=False, getter=_random_state_getter, dependencies='seed') seed = Parameter(DEFAULT_SEED, modulable=True, fallback_default=True, setter=_seed_setter) select_randomly_from_optimal_values = Parameter(False) @@ -1477,8 +1492,8 @@ def __init__(self, objective_function:tc.optional(is_function_type)=None, search_space=None, direction:tc.optional(tc.enum(MAXIMIZE, MINIMIZE))=None, - save_samples:tc.optional(bool)=False, - save_values:tc.optional(bool)=False, + save_samples:tc.optional(bool)=None, + save_values:tc.optional(bool)=None, # tolerance=0., select_randomly_from_optimal_values=None, seed=None, @@ -1967,7 +1982,7 @@ def _function(self, ) if all_values.size != all_samples.shape[-1]: - raise ValueError(f"OptimizationFunction Error: {self}._evaluate returned misatched sizes for " + raise ValueError(f"OptimizationFunction Error: {self}._evaluate returned mismatched sizes for " f"samples and values. This is likely due to a bug in the implementation of " f"{self.__class__} _evaluate method.") diff --git a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py index d433b4bb036..4e269ddbd63 100644 --- a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py @@ -1120,7 +1120,10 @@ def compute_costs(self, intensity, context=None): self.parameters.duration_cost._set(duration_cost, context) all_costs = [intensity_cost, adjustment_cost, duration_cost] - combined_cost = self.combine_costs_function(all_costs, context=context) + + # Combine the costs. Convert to a float because reRedcu + combined_cost = self.combine_costs_function(all_costs, context=context).astype(float) + return max(0.0, combined_cost) def _gen_llvm_function(self, *, ctx:pnlvm.LLVMBuilderContext, diff --git a/psyneulink/core/compositions/parameterestimationcomposition.py b/psyneulink/core/compositions/parameterestimationcomposition.py index 6dcc8e3658d..98b2df8b631 100644 --- a/psyneulink/core/compositions/parameterestimationcomposition.py +++ b/psyneulink/core/compositions/parameterestimationcomposition.py @@ -259,7 +259,7 @@ class ParameterEstimationComposition(Composition): specifies the number of estimates made for a each combination of `parameter ` values (see `num_estimates ` for additional information); it is passed to the ParameterEstimationComposition's `controller ` to set its - `num_estimates ` Parameter. + `num_estimates ` Parameter. num_trials_per_estimate : int : default None specifies an exact number of trials to execute for each run of the `model diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 89a468c09c4..aca10006dae 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1821,12 +1821,11 @@ def test_add_node_with_controller_spec_and_control_mech_but_not_a_controller(sel @pytest.mark.control @pytest.mark.composition @pytest.mark.parametrize("cost, expected, exp_values", [ - # FIX: 11/3/21: NEED TO CHANGE expected (and exp_values?) NOW THAT feature_input_ports IS IMPLEMENTED - (pnl.CostFunctions.NONE, 7.0, [1, 2, 3, 4, 5]), - (pnl.CostFunctions.INTENSITY, 3, [-1.71828183, -5.3890561, -17.08553692, -50.59815003, -143.4131591]), - (pnl.CostFunctions.ADJUSTMENT, 3, [1, 1, 1, 1, 1] ), - (pnl.CostFunctions.INTENSITY | pnl.CostFunctions.ADJUSTMENT, 3, [-1.71828183, -6.3890561, -19.08553692, -53.59815003, -147.4131591]), - (pnl.CostFunctions.DURATION, 3, [-19, -22., -25., -28., -31]), + (pnl.CostFunctions.NONE, 7.0, [3, 4, 5, 6, 7]), + (pnl.CostFunctions.INTENSITY, 3, [0.2817181715409549, -3.3890560989306495, -15.085536923187664, -48.59815003314423, -141.41315910257657]), + (pnl.CostFunctions.ADJUSTMENT, 3, [3, 3, 3, 3, 3] ), + (pnl.CostFunctions.INTENSITY | pnl.CostFunctions.ADJUSTMENT, 3, [0.2817181715409549, -4.389056098930649, -17.085536923187664, -51.59815003314423, -145.41315910257657]), + (pnl.CostFunctions.DURATION, 3, [-17, -20, -23, -26, -29]), # FIXME: combinations with DURATION are broken # (pnl.CostFunctions.DURATION | pnl.CostFunctions.ADJUSTMENT, ,), # (pnl.CostFunctions.ALL, ,), @@ -1853,6 +1852,9 @@ def test_modulation_simple(self, cost, expected, exp_values, comp_mode): allocation_samples=pnl.SampleSpec(start=1, stop=5, step=1), cost_options=cost, ), + + # Need to specify GridSearch since save_values is False by default and we + # going to check these values later in the test. function=pnl.GridSearch(save_values=True) ) ) @@ -1860,7 +1862,7 @@ def test_modulation_simple(self, cost, expected, exp_values, comp_mode): ret = comp.run(inputs={mech: [2]}, num_trials=1, execution_mode=comp_mode) assert np.allclose(ret, expected) if comp_mode == pnl.ExecutionMode.Python: - assert np.allclose([float(x) for x in comp.controller.function.saved_values], exp_values) + assert np.allclose(comp.controller.function.saved_values.flatten(), exp_values) @pytest.mark.benchmark @pytest.mark.control @@ -2659,7 +2661,7 @@ def test_model_based_ocm_after(self, benchmark, mode): ocm = pnl.OptimizationControlMechanism(agent_rep=comp, state_features=[A.input_port], objective_mechanism=objective_mech, - function=pnl.GridSearch(), + function=pnl.GridSearch(save_values=True), control_signals=[control_signal], comp_execution_mode=ocm_mode) # objective_mech.log.set_log_conditions(pnl.OUTCOME) @@ -2710,7 +2712,7 @@ def test_model_based_ocm_before(self, benchmark, mode): ocm = pnl.OptimizationControlMechanism(agent_rep=comp, state_features=[A.input_port], objective_mechanism=objective_mech, - function=pnl.GridSearch(), + function=pnl.GridSearch(save_values=True), control_signals=[control_signal], comp_execution_mode=ocm_mode) # objective_mech.log.set_log_conditions(pnl.OUTCOME) diff --git a/tests/functions/test_integrator.py b/tests/functions/test_integrator.py index 76bc5e53fd8..26600d7cc7c 100644 --- a/tests/functions/test_integrator.py +++ b/tests/functions/test_integrator.py @@ -185,10 +185,10 @@ def test_execute(func, func_mode, variable, noise, params, benchmark): # If we are dealing with a DriftDiffusionIntegrator, noise and time_step_size defaults # have changed since this test was created. Hard code their old values. if 'DriftDiffusionIntegrator' in str(func[0]): - noise = np.sqrt(noise) - params['time_step_size'] = 1.0 + f = func[0](default_variable=variable, noise=np.sqrt(noise), time_step_size=1.0, **params) + else: + f = func[0](default_variable=variable, noise=noise, **params) - f = func[0](default_variable=variable, noise=noise, **params) ex = pytest.helpers.get_func_execution(f, func_mode) ex(variable) diff --git a/tests/projections/test_projection_specifications.py b/tests/projections/test_projection_specifications.py index eaecc1ae938..52358f04f52 100644 --- a/tests/projections/test_projection_specifications.py +++ b/tests/projections/test_projection_specifications.py @@ -137,7 +137,7 @@ def test_multiple_modulatory_projection_specs(self, control_spec, gating_spec, e gating_sig_spec = {gating_spec: [M.output_ports[pnl.DECISION_VARIABLE], M.output_ports[pnl.RESPONSE_TIME]]} if extra_spec: - ctl_sig_spec.update({extra_spec:[M.parameter_ports[pnl.STARTING_POINT]]}) + ctl_sig_spec.update({extra_spec:[M.parameter_ports[pnl.STARTING_VALUE]]}) gating_sig_spec.update({extra_spec:[M.output_ports[pnl.RESPONSE_TIME]]}) ctl_err_msg = '"Both \'PROJECTIONS\' and \'CONTROL\' entries found in specification dict for ' \ '\'ControlSignal\' of \'ControlMechanism-0\'. Must use only one or the other."' From 657cb78efab4989c7d367ca8504875b621a50bd6 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 8 Mar 2022 14:03:20 -0500 Subject: [PATCH 169/285] fix GradientOptimization._function GradientOptimization was invoking the base class _function implementation which is an abstract method now. It now invokes _evaluate instead to get the old behaviour. --- psyneulink/core/components/functions/fitfunctions.py | 4 ++++ .../components/functions/nonstateful/optimizationfunctions.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/functions/fitfunctions.py b/psyneulink/core/components/functions/fitfunctions.py index 7ee1793f859..14711f14569 100644 --- a/psyneulink/core/components/functions/fitfunctions.py +++ b/psyneulink/core/components/functions/fitfunctions.py @@ -7,6 +7,7 @@ from psyneulink.core.scheduling.condition import AtTrialStart from psyneulink.core.globals.parameters import Parameter from psyneulink.core.llvm import ExecutionMode +from psyneulink.core.components.functions.nonstateful.optimizationfunctions import OptimizationFunction import typing @@ -368,6 +369,9 @@ def log_likelihood(**kwargs): return log_likelihood, param_name_map +class MaxLikelihoodEstimatorFunction(OptimizationFunction): + pass + class MaxLikelihoodEstimator: """ Implements a maximum likelihood estimation given a likelihood function diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index e2fc1dab86a..c3c23fffd95 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -1257,7 +1257,7 @@ def _function(self, evaluated; otherwise it is empty. """ - optimal_sample, optimal_value, all_samples, all_values = super()._function(variable=variable, + optimal_sample, optimal_value, all_samples, all_values = super()._evaluate(variable=variable, context=context, params=params, ) From 62860223cfdb7d12fd1e49b8a5d76c4eb23f85d1 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 8 Mar 2022 23:09:24 -0500 Subject: [PATCH 170/285] llvm, mechanism: Clarify comment Signed-off-by: Jan Vesely --- psyneulink/core/components/mechanisms/mechanism.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 736114839c6..8ac92689d02 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2964,8 +2964,8 @@ def _gen_llvm_param_ports_for_obj(self, obj, params_in, ctx, builder, # Filter out param ports without corresponding param for this function param_ports = [self._parameter_ports[param] for param in compilation_params if param in self._parameter_ports] - # Exit early if there's no modulation. LLVM is not aliminating - # the redundant copy created below. + # Exit early if there's no modulation. It's difficult for compiler + # to replace pointer arguments to functions with the source location. if len(param_ports) == 0: return params_in, builder From 157538902cf94f78faa47f97f7fbc93af2dda733 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 8 Mar 2022 23:37:00 -0500 Subject: [PATCH 171/285] llvm, mechanism: Check "easier" parsing specifications earlier. NFC. Potentialy faster execution postponing expensive 'isinstance' call. Signed-off-by: Jan Vesely --- psyneulink/core/components/mechanisms/mechanism.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 8ac92689d02..358b5944932 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1105,7 +1105,7 @@ INITIALIZING, INIT_EXECUTE_METHOD_ONLY, INIT_FUNCTION_METHOD_ONLY, INPUT, \ INPUT_LABELS_DICT, INPUT_PORT, INPUT_PORT_PARAMS, INPUT_PORTS, MECHANISM, MECHANISM_VALUE, \ MECHANISM_COMPONENT_CATEGORY, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_OUTPUT_PORTS, \ - MULTIPLICATIVE_PARAM, \ + MULTIPLICATIVE_PARAM, EXECUTION_COUNT, \ NAME, OUTPUT, OUTPUT_LABELS_DICT, OUTPUT_PORT, OUTPUT_PORT_PARAMS, OUTPUT_PORTS, OWNER_EXECUTION_COUNT, OWNER_VALUE, \ PARAMETER_PORT, PARAMETER_PORT_PARAMS, PARAMETER_PORTS, PROJECTIONS, REFERENCE_VALUE, RESULT, \ TARGET_LABELS_DICT, VALUE, VARIABLE, WEIGHT @@ -3001,14 +3001,14 @@ def _gen_llvm_output_port_parse_variable(self, ctx, builder, port_spec = port._variable_spec if port_spec == OWNER_VALUE: return value + elif port_spec == OWNER_EXECUTION_COUNT: + execution_count = pnlvm.helpers.get_state_ptr(builder, self, mech_state, EXECUTION_COUNT) + return execution_count elif isinstance(port_spec, tuple) and port_spec[0] == OWNER_VALUE: index = port_spec[1]() if callable(port_spec[1]) else port_spec[1] assert index < len(value.type.pointee) return builder.gep(value, [ctx.int32_ty(0), ctx.int32_ty(index)]) - elif port_spec == OWNER_EXECUTION_COUNT: - execution_count = pnlvm.helpers.get_state_ptr(builder, self, mech_state, "execution_count") - return execution_count else: #TODO: support more spec options assert False, "Unsupported OutputPort spec: {} ({})".format(port_spec, value.type) From 1a1c3fe02dd7e74b7077189764d586fdc5784ccf Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 8 Mar 2022 23:03:08 -0500 Subject: [PATCH 172/285] llvm, mechanism: Consolidate construction of port inputs in one place Port inputs are constructed by combining input data and modulations in one structure. This is the case for all types of ports (input, output, parameter), since all can be modulated. Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 65 ++++++++----------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 358b5944932..6a5086ed675 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2882,7 +2882,7 @@ def _get_state_initializer(self, context): return (port_state_init, *mech_state_init) def _gen_llvm_ports(self, ctx, builder, ports, group, - get_output_ptr, fill_input_data, + get_output_ptr, get_input_data_ptr, mech_params, mech_state, mech_input): group_ports = getattr(self, group) ports_param = pnlvm.helpers.get_param_ptr(builder, self, mech_params, group) @@ -2894,13 +2894,24 @@ def _gen_llvm_ports(self, ctx, builder, ports, group, # Find output location builder, p_output = get_output_ptr(builder, i) + builder, p_input_data = get_input_data_ptr(builder, i) - # Allocate the input structure (data + modulation) + # Port inputs are (data, [modulations]), p_input = builder.alloca(p_function.args[2].type.pointee, name=group + "_port_" + str(i) + "_input") + # fill in the data. + p_data = builder.gep(p_input, [ctx.int32_ty(0), ctx.int32_ty(0)]) + + if p_data.type != p_input_data.type: + assert port in self.output_ports + warnings.warn("Shape mismatch: {} parsed value does not match " + "output port: mech value: {} spec: {} parsed {}.".format( + port, self.defaults.value, port._variable_spec, + port.defaults.variable)) + p_data = builder.gep(p_data, [ctx.int32_ty(0), ctx.int32_ty(0)]) # Copy input data to input structure - builder = fill_input_data(builder, p_input, i) + builder.store(builder.load(p_input_data), p_data) # Copy mod_afferent inputs for idx, p_mod in enumerate(port.mod_afferents): @@ -2943,16 +2954,12 @@ def _get_output_ptr(b, i): ptr = b.gep(ip_output, [ctx.int32_ty(0), ctx.int32_ty(i)]) return b, ptr - def _fill_input(b, p_input, i): - ip_in = builder.gep(mech_input, [ctx.int32_ty(0), ctx.int32_ty(i)]) - # Input port inputs are {original parameter, [modulations]}, - # fill in the first one. - data_ptr = builder.gep(p_input, [ctx.int32_ty(0), ctx.int32_ty(0)]) - b.store(b.load(ip_in), data_ptr) - return b + def _get_input_data_ptr(b, i): + ptr = builder.gep(mech_input, [ctx.int32_ty(0), ctx.int32_ty(i)]) + return b, ptr builder = self._gen_llvm_ports(ctx, builder, self.input_ports, "input_ports", - _get_output_ptr, _fill_input, + _get_output_ptr, _get_input_data_ptr, mech_params, mech_state, mech_input) return ip_output, builder @@ -2979,20 +2986,13 @@ def _get_output_ptr(b, i): param_ports[i].source.name) return b, ptr - def _fill_input(b, p_input, i): - param_ptr = pnlvm.helpers.get_param_ptr(b, obj, params_in, - param_ports[i].source.name) - # Parameter port inputs are {original parameter, [modulations]}, - # here we fill in the first one. - data_ptr = builder.gep(p_input, [ctx.int32_ty(0), ctx.int32_ty(0)]) - assert data_ptr.type == param_ptr.type, \ - "Mishandled modulation type for: {} in '{}' in '{}'".format( - param_ports[i].name, obj.name, self.name) - b.store(b.load(param_ptr), data_ptr) - return b + def _get_input_data_ptr(b, i): + ptr = pnlvm.helpers.get_param_ptr(b, obj, params_in, + param_ports[i].source.name) + return b, ptr builder = self._gen_llvm_ports(ctx, builder, param_ports, "_parameter_ports", - _get_output_ptr, _fill_input, + _get_output_ptr, _get_input_data_ptr, mech_params, mech_state, mech_input) return params_out, builder @@ -3019,24 +3019,13 @@ def _get_output_ptr(b, i): ptr = b.gep(mech_out, [ctx.int32_ty(0), ctx.int32_ty(i)]) return b, ptr - def _fill_input(b, s_input, i): - data_ptr = self._gen_llvm_output_port_parse_variable(ctx, b, + def _get_input_data_ptr(b, i): + ptr = self._gen_llvm_output_port_parse_variable(ctx, b, mech_params, mech_state, value, self.output_ports[i]) - # Output port inputs are {original parameter, [modulations]}, - # fill in the first one. - input_ptr = builder.gep(s_input, [ctx.int32_ty(0), ctx.int32_ty(0)]) - if input_ptr.type != data_ptr.type: - port = self.output_ports[i] - warnings.warn("Shape mismatch: {} parsed value does not match " - "output port: mech value: {} spec: {} parsed {}.".format( - port, self.defaults.value, port._variable_spec, - port.defaults.variable)) - input_ptr = builder.gep(input_ptr, [ctx.int32_ty(0), ctx.int32_ty(0)]) - b.store(b.load(data_ptr), input_ptr) - return b + return b, ptr builder = self._gen_llvm_ports(ctx, builder, self.output_ports, "output_ports", - _get_output_ptr, _fill_input, + _get_output_ptr, _get_input_data_ptr, mech_params, mech_state, mech_in) return builder From adfec8a35c75d5b8baf7882c4d812f505e53a7b8 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 9 Mar 2022 12:10:59 -0500 Subject: [PATCH 173/285] llvm, port: Simplify check of port input shapes Drop exception path, exception handling is expensive. Signed-off-by: Jan Vesely --- psyneulink/core/components/ports/port.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index d49ff82ca07..8a5fbc83a57 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -2313,16 +2313,17 @@ def _get_input_struct_type(self, ctx): # Use function input type. The shape should be the same, # however, some functions still need input shape workarounds. func_input_type = ctx.get_input_struct_type(self.function) - try: - # MODIFIED 4/4/20 NEW: [PER JAN] - if len(self.path_afferents) > 0: - assert len(func_input_type) == len(self.path_afferents), \ - "{} shape mismatch: {}\nport:\n\t{}\n\tfunc: {}\npath_afferents: {}".format( - self, func_input_type, self.defaults.variable, - self.function.defaults.variable, len(self.path_afferents)) - # MODIFIED 4/4/20 END - except (PortError): - pass + + # Not all ports have path_afferents property. + len_path_afferents = len(self._get_all_afferents()) - len(self.mod_afferents) + + # Check that either all inputs or none are delivered by projections. + if len_path_afferents > 0: + assert len(func_input_type) == len_path_afferents, \ + "{} shape mismatch: {}\nport:\n\t{}\n\tfunc: {}\npath_afferents: {}".format( + self, func_input_type, self.defaults.variable, + self.function.defaults.variable, len(self.path_afferents)) + input_types = [func_input_type] # Add modulation for mod in self.mod_afferents: From 7a8ddfec080baa65af176b78493cb6a1919fb455 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 9 Mar 2022 14:46:49 -0500 Subject: [PATCH 174/285] llvm, port: Do not create unnecessary copies of port inputs Drop extra struct from the input type if there's no modulation. This allows us to use data inputs directly without creating an extra copy for port input. Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 56 +++++++++++++------ .../control/optimizationcontrolmechanism.py | 18 ++++-- .../ports/modulatorysignals/controlsignal.py | 8 ++- psyneulink/core/components/ports/port.py | 10 +++- 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 6a5086ed675..9283b903153 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2855,7 +2855,11 @@ def _get_output_struct_type(self, ctx): def _get_input_struct_type(self, ctx): # Extract the non-modulation portion of InputPort input struct - input_type_list = [ctx.get_input_struct_type(port).elements[0] for port in self.input_ports] + def _get_data_part_of_input_struct(p): + struct_ty = ctx.get_input_struct_type(p) + return struct_ty.elements[0] if len(p.mod_afferents) > 0 else struct_ty + + input_type_list = [_get_data_part_of_input_struct(port) for port in self.input_ports] # Get modulatory inputs @@ -2896,22 +2900,40 @@ def _gen_llvm_ports(self, ctx, builder, ports, group, builder, p_output = get_output_ptr(builder, i) builder, p_input_data = get_input_data_ptr(builder, i) - # Port inputs are (data, [modulations]), - p_input = builder.alloca(p_function.args[2].type.pointee, - name=group + "_port_" + str(i) + "_input") - # fill in the data. - p_data = builder.gep(p_input, [ctx.int32_ty(0), ctx.int32_ty(0)]) - - if p_data.type != p_input_data.type: - assert port in self.output_ports - warnings.warn("Shape mismatch: {} parsed value does not match " - "output port: mech value: {} spec: {} parsed {}.".format( - port, self.defaults.value, port._variable_spec, - port.defaults.variable)) - p_data = builder.gep(p_data, [ctx.int32_ty(0), ctx.int32_ty(0)]) - - # Copy input data to input structure - builder.store(builder.load(p_input_data), p_data) + + + if len(port.mod_afferents) == 0: + # There's no modulation so the only input is data + if p_input_data.type == p_function.args[2].type: + p_input = p_input_data + else: + assert port in self.output_ports + # Ports always take at least 2d input. However, parsing + # the function result can get us 1d structure. + # Casting the pointer is LLVM way of adding a dimension + assert len(p_function.args[2].type.pointee) == 1 + assert p_function.args[2].type.pointee.element == p_input_data.type.pointee + p_input = builder.bitcast(p_input_data, p_function.args[2].type) + + else: + # Port input structure is: (data, [modulations]), + p_input = builder.alloca(p_function.args[2].type.pointee, + name=group + "_port_" + str(i) + "_input") + # fill in the data. + p_data = builder.gep(p_input, [ctx.int32_ty(0), ctx.int32_ty(0)]) + + if p_data.type != p_input_data.type: + assert port in self.output_ports + # Ports always take at least 2d input. However, parsing + # the function result can get us 1d structure. + warnings.warn("Shape mismatch: {} parsed value does not match " + "output port: mech value: {} spec: {} parsed {}.".format( + port, self.defaults.value, port._variable_spec, + port.defaults.variable)) + p_data = builder.gep(p_data, [ctx.int32_ty(0), ctx.int32_ty(0)]) + + # Copy input data to input structure + builder.store(builder.load(p_input_data), p_data) # Copy mod_afferent inputs for idx, p_mod in enumerate(port.mod_afferents): diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index da8e97bc5fe..a73f2723211 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -2852,8 +2852,15 @@ def _gen_llvm_net_outcome_function(self, *, ctx, tags=frozenset()): # copy allocation_sample, the input is 1-element array in a struct data_in = builder.gep(allocation_sample, [ctx.int32_ty(0), ctx.int32_ty(i)]) - data_out = builder.gep(op_in, [ctx.int32_ty(0), ctx.int32_ty(0), - ctx.int32_ty(0)]) + + # Port input struct is {data, modulation} if modulation is present, + # otherwise it's just data + if len(op.mod_afferents) > 0: + data_out = builder.gep(op_in, [ctx.int32_ty(0), ctx.int32_ty(0), + ctx.int32_ty(0)]) + else: + data_out = builder.gep(op_in, [ctx.int32_ty(0), ctx.int32_ty(0)]) + if data_in.type != data_out.type: warnings.warn(f"Shape mismatch: Allocation sample '{i}' " f"({self.parameters.control_allocation_search_space.get()}) " @@ -3115,9 +3122,10 @@ def _gen_llvm_invoke_function(self, ctx, builder, function, params, context, var def _gen_llvm_output_port_parse_variable(self, ctx, builder, params, context, value, port): i = self.output_ports.index(port) - # Allocate the only member of the port input struct - oport_input = builder.alloca(ctx.get_input_struct_type(port).elements[0], - name="output_port_in") + # Allocate the data member of the port input struct + port_in_ty = ctx.get_input_struct_type(port) + data_in_ty = port_in_ty if len(port.mod_afferents) == 0 else port_in_ty.elements[0] + oport_input = builder.alloca(data_in_ty, name="output_port_{}_input".format(i)) # FIXME: workaround controller signals occasionally being 2d dest_ptr = pnlvm.helpers.unwrap_2d_array(builder, oport_input) dest_ptr = builder.gep(dest_ptr, [ctx.int32_ty(0), ctx.int32_ty(0)]) diff --git a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py index d433b4bb036..b7d7f07598d 100644 --- a/psyneulink/core/components/ports/modulatorysignals/controlsignal.py +++ b/psyneulink/core/components/ports/modulatorysignals/controlsignal.py @@ -1168,8 +1168,12 @@ def _gen_llvm_costs(self, *, ctx:pnlvm.LLVMBuilderContext, tags:frozenset): ifunc_state = pnlvm.helpers.get_state_ptr(builder, self.function, func_state, "intensity_cost_fct") - # Port input is always struct { data input, modulations } - ifunc_in = builder.gep(arg_in, [ctx.int32_ty(0), ctx.int32_ty(0)]) + # Port input is struct { data input, modulations } if there are modulations, + # otherwise it's just data_input + if len(self.mod_afferents) > 0: + ifunc_in = builder.gep(arg_in, [ctx.int32_ty(0), ctx.int32_ty(0)]) + else: + ifunc_in = arg_in # point output to the proper slot in comb func input assert cost_funcs == 0, "Intensity should eb the first cost function!" ifunc_out = builder.gep(cfunc_in, [ctx.int32_ty(0), ctx.int32_ty(cost_funcs)]) diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index 8a5fbc83a57..84a5b8b9627 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -2324,6 +2324,11 @@ def _get_input_struct_type(self, ctx): self, func_input_type, self.defaults.variable, self.function.defaults.variable, len(self.path_afferents)) + if len(self.mod_afferents) == 0: + # Not need to wrap inputs of non-modulated ports inside mechanisms + # This makes sure the port input matches port data input and avoids a copy + return func_input_type + input_types = [func_input_type] # Add modulation for mod in self.mod_afferents: @@ -2396,7 +2401,10 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, assert len(arg_out.type.pointee) == 1 arg_out = builder.gep(arg_out, [ctx.int32_ty(0), ctx.int32_ty(0)]) # Extract the data part of input - f_input = builder.gep(arg_in, [ctx.int32_ty(0), ctx.int32_ty(0)]) + if len(self.mod_afferents) == 0: + f_input = arg_in + else: + f_input = builder.gep(arg_in, [ctx.int32_ty(0), ctx.int32_ty(0)]) f_state = pnlvm.helpers.get_state_ptr(builder, self, state, "function") builder.call(port_f, [f_params, f_state, f_input, arg_out]) return builder From 7273b9da63121f678a8d4714b9a92dcb1e9df288 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 9 Mar 2022 17:28:50 -0500 Subject: [PATCH 175/285] llvm, mechanism/OCM: Reuse existing port parsing method OCM extracts 'optimal_samples' before returning its value, do that before calling the generic parser routine. Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 33 +++++++------------ .../control/optimizationcontrolmechanism.py | 17 +++------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 9283b903153..c36446c06b7 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2896,11 +2896,9 @@ def _gen_llvm_ports(self, ctx, builder, ports, group, for i, port in enumerate(ports): p_function = ctx.import_llvm_function(port) - # Find output location - builder, p_output = get_output_ptr(builder, i) + # Find input and output locations builder, p_input_data = get_input_data_ptr(builder, i) - - + builder, p_output = get_output_ptr(builder, i) if len(port.mod_afferents) == 0: # There's no modulation so the only input is data @@ -2909,30 +2907,23 @@ def _gen_llvm_ports(self, ctx, builder, ports, group, else: assert port in self.output_ports # Ports always take at least 2d input. However, parsing - # the function result can get us 1d structure. - # Casting the pointer is LLVM way of adding a dimension - assert len(p_function.args[2].type.pointee) == 1 - assert p_function.args[2].type.pointee.element == p_input_data.type.pointee + # the function result can result in 1d structure or scalar + # Casting the pointer is LLVM way of adding dimensions + array_1d = pnlvm.ir.ArrayType(p_input_data.type.pointee, 1) + array_2d = pnlvm.ir.ArrayType(array_1d, 1) + assert array_1d == p_function.args[2].type.pointee or array_2d == p_function.args[2].type.pointee, \ + "{} vs.{}".format(p_function.args[2].type.pointee, p_input_data.type.pointee) p_input = builder.bitcast(p_input_data, p_function.args[2].type) else: # Port input structure is: (data, [modulations]), p_input = builder.alloca(p_function.args[2].type.pointee, name=group + "_port_" + str(i) + "_input") - # fill in the data. + # Fill in the data. + # FIXME: We can potentially hit the same dimensionality issue + # as above, but it's more difficult to manifest and + # not even new tests that modulate output ports hit it. p_data = builder.gep(p_input, [ctx.int32_ty(0), ctx.int32_ty(0)]) - - if p_data.type != p_input_data.type: - assert port in self.output_ports - # Ports always take at least 2d input. However, parsing - # the function result can get us 1d structure. - warnings.warn("Shape mismatch: {} parsed value does not match " - "output port: mech value: {} spec: {} parsed {}.".format( - port, self.defaults.value, port._variable_spec, - port.defaults.variable)) - p_data = builder.gep(p_data, [ctx.int32_ty(0), ctx.int32_ty(0)]) - - # Copy input data to input structure builder.store(builder.load(p_input_data), p_data) # Copy mod_afferent inputs diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index a73f2723211..97c442aec42 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -3120,18 +3120,11 @@ def _gen_llvm_invoke_function(self, ctx, builder, function, params, context, var return fun_out, builder - def _gen_llvm_output_port_parse_variable(self, ctx, builder, params, context, value, port): - i = self.output_ports.index(port) - # Allocate the data member of the port input struct - port_in_ty = ctx.get_input_struct_type(port) - data_in_ty = port_in_ty if len(port.mod_afferents) == 0 else port_in_ty.elements[0] - oport_input = builder.alloca(data_in_ty, name="output_port_{}_input".format(i)) - # FIXME: workaround controller signals occasionally being 2d - dest_ptr = pnlvm.helpers.unwrap_2d_array(builder, oport_input) - dest_ptr = builder.gep(dest_ptr, [ctx.int32_ty(0), ctx.int32_ty(0)]) - val_ptr = builder.gep(value, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(i)]) - builder.store(builder.load(val_ptr), dest_ptr) - return oport_input + def _gen_llvm_output_port_parse_variable(self, ctx, builder, params, state, value, port): + # The function returns (sample_optimal, value_optimal), + # but the value of mechanism is only 'sample_optimal' + value = builder.gep(value, [ctx.int32_ty(0), ctx.int32_ty(0)]) + return super()._gen_llvm_output_port_parse_variable(ctx, builder, params, state, value, port) @property def agent_rep_type(self): From adaaa4d694a7f2a2003772baf4a3b0afcc41da01 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 9 Mar 2022 17:34:26 -0500 Subject: [PATCH 176/285] llvm, functions/GaussianDistort: Add name to allocated output location Signed-off-by: Jan Vesely --- .../core/components/functions/nonstateful/transferfunctions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/functions/nonstateful/transferfunctions.py b/psyneulink/core/components/functions/nonstateful/transferfunctions.py index e04beea9d19..d35deceaa5d 100644 --- a/psyneulink/core/components/functions/nonstateful/transferfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/transferfunctions.py @@ -2252,7 +2252,7 @@ def _gen_llvm_transfer(self, builder, index, ctx, vi, vo, params, state, *, tags scale = pnlvm.helpers.load_extract_scalar_array_one(builder, scale_ptr) offset = pnlvm.helpers.load_extract_scalar_array_one(builder, offset_ptr) - rvalp = builder.alloca(ptri.type.pointee) + rvalp = builder.alloca(ptri.type.pointee, name="random_out") rand_state_ptr = ctx.get_random_state_ptr(builder, self, state, params) normal_f = ctx.get_normal_dist_function_by_state(rand_state_ptr) builder.call(normal_f, [rand_state_ptr, rvalp]) From 32ac8277fad337cc31038adf8c88e2adee565e2f Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 9 Mar 2022 22:21:24 -0500 Subject: [PATCH 177/285] tests: Add basic tests for different parameter port modulation options Signed-off-by: Jan Vesely --- tests/composition/test_control.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 33b9f1f792b..ad87f3e1c00 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1774,18 +1774,24 @@ def test_recurrent_control(self, comp_mode): @pytest.mark.control @pytest.mark.composition - def test_control_of_mech_port(self, comp_mode): + @pytest.mark.parametrize("modulation, expected", [ + (pnl.OVERRIDE, 0.375), + (pnl.DISABLE, 0.4375), + (pnl.MULTIPLICATIVE, 0.484375), + (pnl.ADDITIVE, 0.25), + ]) + def test_control_of_mech_port(self, comp_mode, modulation, expected): mech = pnl.TransferMechanism(termination_threshold=0.1, execute_until_finished=True, integrator_mode=True) control_mech = pnl.ControlMechanism( - control_signals=pnl.ControlSignal(modulation=pnl.OVERRIDE, + control_signals=pnl.ControlSignal(modulation=modulation, modulates=(pnl.TERMINATION_THRESHOLD, mech))) comp = pnl.Composition() comp.add_nodes([(mech, pnl.NodeRole.INPUT), (control_mech, pnl.NodeRole.INPUT)]) inputs = {mech:[[0.5]], control_mech:[0.2]} results = comp.run(inputs=inputs, num_trials=1, execution_mode=comp_mode) - assert np.allclose(comp.results, [[[0.375]]]) + assert np.allclose(results, expected) @pytest.mark.control @pytest.mark.composition From 1c402d4ae5faf2b5fc925cd322f21773487aa9ac Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 9 Mar 2022 22:26:02 -0500 Subject: [PATCH 178/285] tests: Add basic tests for different input port modulation options Signed-off-by: Jan Vesely --- tests/composition/test_control.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index ad87f3e1c00..a3416b6fae9 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1793,6 +1793,25 @@ def test_control_of_mech_port(self, comp_mode, modulation, expected): results = comp.run(inputs=inputs, num_trials=1, execution_mode=comp_mode) assert np.allclose(results, expected) + @pytest.mark.control + @pytest.mark.composition + @pytest.mark.parametrize("modulation, expected", [ + (pnl.OVERRIDE, 0.2), + (pnl.DISABLE, 0.5), + (pnl.MULTIPLICATIVE, 0.1), + (pnl.ADDITIVE, 0.7), + ]) + def test_control_of_mech_input_port(self, comp_mode, modulation, expected): + mech = pnl.TransferMechanism() + control_mech = pnl.ControlMechanism( + control_signals=pnl.ControlSignal(modulation=modulation, + modulates=mech.input_port)) + comp = pnl.Composition() + comp.add_nodes([(mech, pnl.NodeRole.INPUT), (control_mech, pnl.NodeRole.INPUT)]) + inputs = {mech:[[0.5]], control_mech:[0.2]} + results = comp.run(inputs=inputs, num_trials=1, execution_mode=comp_mode) + assert np.allclose(results, expected) + @pytest.mark.control @pytest.mark.composition def test_add_node_with_controller_spec_and_control_mech_but_not_a_controller(self): From 1d5e71d8d46c9f643efcc8c5691442953c8aead8 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 9 Mar 2022 23:36:52 -0500 Subject: [PATCH 179/285] tests: Add basic tests for different input port modulation options Signed-off-by: Jan Vesely --- psyneulink/core/llvm/codegen.py | 1 - tests/composition/test_control.py | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index 09284627ff4..c09bfebef08 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -658,7 +658,6 @@ def gen_node_wrapper(ctx, composition, node, *, tags:frozenset): warnings.warn("Shape mismatch: Projection ({}) results does not match the receiver state({}) input: {} vs. {}".format(proj, proj.receiver, proj.defaults.value, proj.receiver.defaults.variable)) # Check that this workaround applies only to projections to inner composition # that are off by one dimension, but in the 'wrong' direction (we need to add one dim). - assert not is_mech assert len(proj_function.args[3].type.pointee) == 1 assert proj_function.args[3].type.pointee.element == proj_out.type.pointee diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index a3416b6fae9..834cc5a5ce5 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1812,6 +1812,26 @@ def test_control_of_mech_input_port(self, comp_mode, modulation, expected): results = comp.run(inputs=inputs, num_trials=1, execution_mode=comp_mode) assert np.allclose(results, expected) + @pytest.mark.control + @pytest.mark.composition + @pytest.mark.parametrize("modulation, expected", [ + (pnl.OVERRIDE, 0.2), + (pnl.DISABLE, 0.5), + (pnl.MULTIPLICATIVE, 0.1), + (pnl.ADDITIVE, 0.7), + ]) + @pytest.mark.parametrize("specification", [ pnl.OWNER_VALUE, (pnl.OWNER_VALUE, 0)]) + def test_control_of_mech_output_port(self, comp_mode, modulation, expected, specification): + mech = pnl.TransferMechanism(output_ports=[pnl.OutputPort(variable=specification)]) + control_mech = pnl.ControlMechanism( + control_signals=pnl.ControlSignal(modulation=modulation, + modulates=mech.output_port)) + comp = pnl.Composition() + comp.add_nodes([(mech, pnl.NodeRole.INPUT), (control_mech, pnl.NodeRole.INPUT)]) + inputs = {mech:[[0.5]], control_mech:[0.2]} + results = comp.run(inputs=inputs, num_trials=1, execution_mode=comp_mode) + assert np.allclose(results, expected) + @pytest.mark.control @pytest.mark.composition def test_add_node_with_controller_spec_and_control_mech_but_not_a_controller(self): From 1e390abcbdbc709480aefef52a86b16727a5bfee Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 11 Mar 2022 01:01:30 -0500 Subject: [PATCH 180/285] llvm/cuda: Enable function inlining in CUDA compilation path (#2338) The threshold is empirically determined using the predator-prey model. Number of function calls (static) changes: model | before | inline0 | inline4 | inline6 | inline7 | inline8 p-p | 287 | 195 | 162 | 123 | 121 | 125 s-f | 413 | 221 | 206 | 156 | 155 | 164 This significantly reduces GPU stalls because of instruction fetch (measured on P620) (pp-MT/pp-Philox): 10.99%/18.21% -> 5.06%/8.46% (sf-MT/sf-Philox): 36.72%/38.96% -> 19.1%/22.2% as well as the amount of private data read/written. This combines to ~15-20% (pp) improvement in kernel runtime. Switch 'opt' param to apply to PTX backend rather than target-independent optimizer. We don't have a good way to optimize for register pressure so even O1 results in a significant increase in per-thread register usage. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/jit_engine.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/llvm/jit_engine.py b/psyneulink/core/llvm/jit_engine.py index ce1b9a66e6f..23815b9aa45 100644 --- a/psyneulink/core/llvm/jit_engine.py +++ b/psyneulink/core/llvm/jit_engine.py @@ -103,12 +103,14 @@ def _cpu_jit_constructor(): def _ptx_jit_constructor(): _binding_initialize() - opt_level = int(debug_env.get('opt', 0)) + opt_level = int(debug_env.get('opt', 2)) - # PassManagerBuilder can be shared + # PassManagerBuilder is used only for inlining simple functions __pass_manager_builder = binding.PassManagerBuilder() - __pass_manager_builder.opt_level = opt_level - __pass_manager_builder.size_level = 1 # Try to reduce size to reduce PTX parsing time + __pass_manager_builder.opt_level = 0 + __pass_manager_builder.size_level = 1 + # The threshold of '7' is empirically selected. + __pass_manager_builder.inlining_threshold = 7 # Use default device # TODO: Add support for multiple devices @@ -116,7 +118,7 @@ def _ptx_jit_constructor(): __ptx_sm = "sm_{}{}".format(__compute_capability[0], __compute_capability[1]) # Create compilation target, use 64bit triple __ptx_target = binding.Target.from_triple("nvptx64-nvidia-cuda") - __ptx_target_machine = __ptx_target.create_target_machine(cpu=__ptx_sm) + __ptx_target_machine = __ptx_target.create_target_machine(cpu=__ptx_sm, opt=opt_level) __ptx_pass_manager = binding.ModulePassManager() __ptx_target_machine.add_analysis_passes(__ptx_pass_manager) From 84256d2b7c4aead2579784a816cc08a02f836362 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 11 Mar 2022 22:55:27 -0500 Subject: [PATCH 181/285] llvm, component: Restrict execution counts to mechanisms No other component type is using 'execution_until_finished'. Significantly reduces size of model mutable state: pp: 15.75/6.28kB -> 10.7/1.32kB sf: 17.7/10.57kB -> 8.81/1.71kB --- psyneulink/core/components/component.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 29b6bb1dea5..487badae634 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1312,18 +1312,22 @@ def __deepcopy__(self, memo): def _get_compilation_state(self): # FIXME: MAGIC LIST, Use stateful tag for this whitelist = {"previous_time", "previous_value", "previous_v", - "previous_w", "random_state", "is_finished_flag", - "num_executions_before_finished", "num_executions", - "execution_count", "value", "input_ports", "output_ports"} - blacklist = { # References to other components - "objective_mechanism", "agent_rep", "projections"} - # Only mechanisms use "value" state - if not hasattr(self, 'ports'): - blacklist.add("value") + "previous_w", "random_state", + "input_ports", "output_ports"} + # Prune subcomponents (which are enabled by type rather than a list) + # that should be omitted + blacklist = { "objective_mechanism", "agent_rep", "projections"} + + # Only mechanisms use "value" state, can execute 'until finished', + # and need to track executions + if hasattr(self, 'ports'): + whitelist.update({"value", "num_executions_before_finished", + "num_executions", "is_finished_flag", + "execution_count"}) # Only mechanisms and compositions need 'num_executions' - if not hasattr(self, 'ports') and not hasattr(self, 'nodes'): - blacklist.add("num_executions") + if hasattr(self, 'nodes'): + whitelist.add("num_executions") def _is_compilation_state(p): #FIXME: This should use defaults instead of 'p.get' From ade329eb189f6af0366609f058a52119e07575c1 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Fri, 11 Mar 2022 20:58:32 -0500 Subject: [PATCH 182/285] llvm, component: Drop unused parameters from compiled structure Structural parameter, not used during computation. ro params: pp: 5.96kB -> 4.83kB, sf: 5.77kB -> 4.02kB Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 487badae634..3608d1b3c3e 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1390,17 +1390,25 @@ def _get_compilation_params(self): "state_feature_specs", # Reference to other components "objective_mechanism", "agent_rep", "projections", - # Shape mismatch - "auto", "hetero", "cost", "costs", "combined_costs", - "control_signal", + "outcome_input_ports", "state_input_ports", # autodiff specific types "pytorch_representation", "optimizer", # duplicate "allocation_samples", "control_allocation_search_space", # not used in computation + "auto", "hetero", "cost", "costs", "combined_costs", + "control_signal", "intensity", "has_recurrent_input_port", "enable_learning", "enable_output_type_conversion", "changes_shape", - "output_type", "bounds"} + "output_type", "bounds", "internal_only", + "require_projection_in_composition", "default_input", + "shadow_inputs", "compute_reconfiguration_cost", + "reconfiguration_cost", "net_outcome", "outcome", + "adjustment_cost", "intensity_cost", "duration_cost", + "enabled_cost_functions", "control_signal_costs", + "default_allocation", "same_seed_for_all_allocations", + "search_statefulness", "initial_seed", "combine" + } # Mechanism's need few extra entires: # * matrix -- is never used directly, and is flatened below # * integration rate -- shape mismatch with param port input From cae6697b91ae7668192c70a4c46ca21bbd34f559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 00:55:17 +0000 Subject: [PATCH 183/285] requirements: update pytest requirement from <7.0.2 to <7.1.1 (#2341) --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index a3474d55688..09f40ce6e8b 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,5 +1,5 @@ jupyter<=1.0.0 -pytest<7.0.2 +pytest<7.1.1 pytest-benchmark<3.4.2 pytest-cov<3.0.1 pytest-helpers-namespace<2021.12.30 From 3c8449980d6d826d0015a1adb582fd729cb1c8f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 04:08:36 +0000 Subject: [PATCH 184/285] requirements: update pytest-pydocstyle requirement (#2342) --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 09f40ce6e8b..eb9b2ffbffc 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -5,5 +5,5 @@ pytest-cov<3.0.1 pytest-helpers-namespace<2021.12.30 pytest-profiling<=1.7.0 pytest-pycodestyle<=2.2.0 -pytest-pydocstyle<=2.2.0 +pytest-pydocstyle<2.4.0 pytest-xdist<2.6.0 From 2cd6de2690300ca73a41c012e0798c4e919088b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 05:43:06 +0000 Subject: [PATCH 185/285] requirements: update pytest-pycodestyle requirement (#2340) --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index eb9b2ffbffc..5e11ce032d6 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -4,6 +4,6 @@ pytest-benchmark<3.4.2 pytest-cov<3.0.1 pytest-helpers-namespace<2021.12.30 pytest-profiling<=1.7.0 -pytest-pycodestyle<=2.2.0 +pytest-pycodestyle<2.4.0 pytest-pydocstyle<2.4.0 pytest-xdist<2.6.0 From 0210bfe4edf6b1c67b1825c35c6447ad86153fec Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 16 Mar 2022 13:52:13 -0400 Subject: [PATCH 186/285] llvm: Implement printf helper on windows (#2346) Restrict graceful fail paths to library/symbol not found conditions. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/helpers.py | 23 ++++++++++++++++------- tests/llvm/test_helpers.py | 13 ++++++++----- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/psyneulink/core/llvm/helpers.py b/psyneulink/core/llvm/helpers.py index dd73fdd5e16..b97f77480af 100644 --- a/psyneulink/core/llvm/helpers.py +++ b/psyneulink/core/llvm/helpers.py @@ -10,8 +10,12 @@ from contextlib import contextmanager from ctypes import util +import warnings +import sys from llvmlite import ir +import llvmlite.binding as llvm + from .debug import debug_env from psyneulink.core.scheduling.condition import All, AllHaveRun, Always, Any, AtPass, AtTrial, BeforeNCalls, AtNCalls, AfterNCalls, \ @@ -383,14 +387,19 @@ def call_elementwise_operation(ctx, builder, x, operation, output_ptr): def printf(builder, fmt, *args, override_debug=False): if "print_values" not in debug_env and not override_debug: return + #FIXME: Fix builtin printf and use that instead of this - try: - import llvmlite.binding as llvm - libc = util.find_library("c") - llvm.load_library_permanently(libc) - # Address will be none if the symbol is not found - printf_address = llvm.address_of_symbol("printf") - except Exception as e: + libc_name = "msvcrt" if sys.platform == "win32" else "c" + libc = util.find_library(libc_name) + if libc is None: + warnings.warn("Standard libc library not found, 'printf' not available!") + return + + llvm.load_library_permanently(libc) + # Address will be none if the symbol is not found + printf_address = llvm.address_of_symbol("printf") + if printf_address is None: + warnings.warn("'printf' symbol not found in libc, 'printf' not available!") return # Direct pointer constants don't work diff --git a/tests/llvm/test_helpers.py b/tests/llvm/test_helpers.py index d5a9e9112fa..6862c784d00 100644 --- a/tests/llvm/test_helpers.py +++ b/tests/llvm/test_helpers.py @@ -220,10 +220,9 @@ def test_helper_all_close(mode, var1, var2, atol, rtol): @pytest.mark.llvm @pytest.mark.parametrize("ir_argtype,format_spec,values_to_check", [ (pnlvm.ir.IntType(32), "%u", range(0, 20)), - (pnlvm.ir.IntType(64), "%ld", [int(-4E10), int(-3E10), int(-2E10)]), + (pnlvm.ir.IntType(64), "%lld", [int(-4E10), int(-3E10), int(-2E10)]), (pnlvm.ir.DoubleType(), "%lf", [x *.5 for x in range(0, 5)]), ], ids=["i32", "i64", "double"]) -@pytest.mark.skipif(sys.platform == 'win32', reason="Loading C library is complicated on windows") def test_helper_printf(capfd, ir_argtype, format_spec, values_to_check): format_str = f"Hello {(format_spec+' ')*len(values_to_check)} \n" with pnlvm.LLVMBuilderContext.get_current() as ctx: @@ -240,12 +239,16 @@ def test_helper_printf(capfd, ir_argtype, format_spec, values_to_check): bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) - # Printf is buffered in libc. bin_f() - libc = ctypes.util.find_library("c") + # Printf is buffered in libc. + libc = ctypes.util.find_library("msvcrt" if sys.platform == "win32" else "c") libc = ctypes.CDLL(libc) libc.fflush(0) - assert capfd.readouterr().out == format_str % tuple(values_to_check) + + # Convert format specifier to Python compatible + python_format_spec = {"%lld":"%ld"}.get(format_spec, format_spec) + python_format_str = f"Hello {(python_format_spec+' ')*len(values_to_check)} \n" + assert capfd.readouterr().out == python_format_str % tuple(values_to_check) class TestHelperTypegetters: FLOAT_TYPE = pnlvm.ir.FloatType() From ef07e5712418c36dafe92f0692d10e72ff587e93 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 16 Mar 2022 22:29:08 -0400 Subject: [PATCH 187/285] Refactor/ocm/state features all as input ports (#2345) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • controlmechanism.py, optimizationcontrolmechanism.py: - _instantiate_monitor_for_control_input_ports -> _parse_monitor_control_input_ports - refactored to support allow_probes option on ocm * - * • composition.py: __init__: move controller to after add_nodes and add_linear_pathway * - * - test_control: only test_hanging_control_spec_outer_controller not passing * - * - * - * - * - * - * • composition.py: _instantiate_control_projections: weird requirement for double-call to controller._instantiate_control_signal * • test_paremtercomposition.py: restored parameter spec that causes crash ('threshold',Decision2) * ª Attempt to fix problem with partially overlapping local and ocm control specs - composition.py - _get_control_signals_for_composition: (see 11/20/21) - added (but commented out change) to "if node.controller" to "if not node.controller" - changed append to extend - _instantiation_control_projection: - got rid of try and except double-call to controller._instantiate_control_signals - outdented call to self.controller._activate_projections_for_composition at end - controlmechanism.py: - _check_for_duplicates: add warning and return duplicates - optimizationcontrolmechanism._instantiate_control_signals: - add call to self.agent_rep._get_control_signals_for_composition() to get local control specs (on mechs in comp) - eliminate duplicates with control_signal specs on OCM - instantiate local + ocm control_signals - parameterestimationcomposition.py - added context to various calls * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- doc_requirements.txt | 2 +- psyneulink/core/components/component.py | 3 +- .../nonstateful/distributionfunctions.py | 10 +- .../functions/stateful/integratorfunctions.py | 37 +- .../core/components/mechanisms/mechanism.py | 59 +- .../control/optimizationcontrolmechanism.py | 779 ++++++++++-------- .../processing/objectivemechanism.py | 2 +- psyneulink/core/components/ports/inputport.py | 20 +- .../core/components/ports/outputport.py | 12 +- .../core/components/ports/parameterport.py | 4 + psyneulink/core/components/ports/port.py | 20 +- psyneulink/core/compositions/composition.py | 645 ++++++++++----- .../parameterestimationcomposition.py | 10 +- psyneulink/core/globals/keywords.py | 4 +- requirements.txt | 2 +- tests/composition/test_composition.py | 67 +- tests/composition/test_control.py | 281 ++++--- tests/composition/test_learning.py | 2 +- 18 files changed, 1225 insertions(+), 734 deletions(-) diff --git a/doc_requirements.txt b/doc_requirements.txt index 53b11bf040e..043ea79e043 100644 --- a/doc_requirements.txt +++ b/doc_requirements.txt @@ -1,3 +1,3 @@ psyneulink-sphinx-theme<1.2.3.1 sphinx<4.2.1 -sphinx_autodoc_typehints<1.18.0 +sphinx_autodoc_typehints<1.16.0 diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 3608d1b3c3e..267387fd0dc 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -789,7 +789,8 @@ class Component(JSONDumpable, metaclass=ComponentsMeta): --------- default_variable : scalar, list or array : default [[0]] - specifies template for the input to the Component's `function `. + specifies template for the input to the Component's `function `, and the value used as the + input to the Component if none is provided on execution (see `Component_Variable` for additional information). size : int, list or np.ndarray of ints : default None specifies default_variable as array(s) of zeros if **default_variable** is not passed as an argument; diff --git a/psyneulink/core/components/functions/nonstateful/distributionfunctions.py b/psyneulink/core/components/functions/nonstateful/distributionfunctions.py index 4171530ddb5..80c979fcb50 100644 --- a/psyneulink/core/components/functions/nonstateful/distributionfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/distributionfunctions.py @@ -965,24 +965,24 @@ class DriftDiffusionAnalytical(DistributionFunction): # ----------------------- drift_rate : float, list or 1d array : default 1.0 specifies the drift_rate of the drift diffusion process. If it is a list or array, - it must be the same length as `default_variable `. + it must be the same length as `default_variable `. threshold : float, list or 1d array : default 1.0 specifies the threshold (boundary) of the drift diffusion process. If it is a list or array, - it must be the same length as `default_variable `. + it must be the same length as `default_variable `. starting_value : float, list or 1d array : default 1.0 specifies the initial value of the decision variable for the drift diffusion process. If it is a list or - array, it must be the same length as `default_variable `. + array, it must be the same length as `default_variable `. noise : float, list or 1d array : default 0.0 specifies the noise term (corresponding to the diffusion component) of the drift diffusion process. If it is a float, it must be a number from 0 to 1. If it is a list or array, it must be the same length as - `default_variable ` and all elements must be floats from 0 to 1. + `default_variable ` and all elements must be floats from 0 to 1. non_decision_time : float, list or 1d array : default 0.2 specifies the non-decision time for solution. If it is a float, it must be a number from 0 to 1. If it is a - list or array, it must be the same length as `default_variable ` and all + list or array, it must be the same length as `default_variable ` and all elements must be floats from 0 to 1. params : Dict[param keyword: param value] : default None diff --git a/psyneulink/core/components/functions/stateful/integratorfunctions.py b/psyneulink/core/components/functions/stateful/integratorfunctions.py index dab0c47c7c5..aa154c43f43 100644 --- a/psyneulink/core/components/functions/stateful/integratorfunctions.py +++ b/psyneulink/core/components/functions/stateful/integratorfunctions.py @@ -462,8 +462,7 @@ class AccumulatorIntegrator(IntegratorFunction): # ---------------------------- initializer : float, list or 1d array : default 0.0 specifies starting value(s) for integration. If it is a list or array, it must be the same length as - `default_variable ` (see `initializer - ` for details). + `variable ` (see `initializer ` for details). params : Dict[param keyword: param value] : default None a `parameter dictionary ` that specifies the parameters for the @@ -741,8 +740,7 @@ class SimpleIntegrator(IntegratorFunction): # --------------------------------- initializer : float, list or 1d array : default 0.0 specifies starting value(s) for integration; if it is a list or array, it must be the same length as - `default_variable ` (see `initializer ` - for details). + `variable ` (see `initializer ` for details). params : Dict[param keyword: param value] : default None a `parameter dictionary ` that specifies the parameters for the @@ -970,8 +968,7 @@ class AdaptiveIntegrator(IntegratorFunction): # ------------------------------- initializer : float, list or 1d array : default 0.0 specifies starting value(s) for integration. If it is a list or array, it must be the same length as - `default_variable ` (see `initializer ` - for details). + `variable ` (see `initializer ` for details). params : Dict[param keyword: param value] : default None a `parameter dictionary ` that specifies the parameters for the @@ -1880,9 +1877,8 @@ class InteractiveActivationIntegrator(IntegratorFunction): # ------------------ ` (see `noise ` for details). initializer : float, list or 1d array : default 0.0 - specifies starting value(s) for integration. If it is a list or array, it must be the same length as - `default_variable ` - (see `initializer ` for details). + specifies starting value(s) for integration. If it is a list or array, it must be the same length as `variable + ` (see `initializer ` for details). params : Dict[param keyword: param value] : default None a `parameter dictionary ` that specifies the parameters for the @@ -2222,9 +2218,8 @@ class DriftDiffusionIntegrator(IntegratorFunction): # ------------------------- ` for details. initializer : float, list or 1d array : default 0.0 - specifies starting value(s) for integration. If it is a list or array, it must be the same length as - `default_variable ` (see `initializer ` - for details). + specifies starting value(s) for integration. If it is a list or array, it must be the same length as `variable + ` (see `initializer ` for details). params : Dict[param keyword: param value] : default None a `parameter dictionary ` that specifies the parameters for the @@ -3263,9 +3258,8 @@ class OrnsteinUhlenbeckIntegrator(IntegratorFunction): # ---------------------- ` for details. initializer : float, list or 1d array : default 0.0 - specifies starting value(s) for integration. If it is a list or array, it must be the same length as - `default_variable ` (see `initializer ` - for details). + specifies starting value(s) for integration. If it is a list or array, it must be the same length as `variable + ` (see `initializer ` for details). params : Dict[param keyword: param value] : default None a `parameter dictionary ` that specifies the parameters for the @@ -3624,9 +3618,8 @@ class LeakyCompetingIntegrator(IntegratorFunction): # ------------------------- ` for details. initializer : float, list or 1d array : default 0.0 - specifies starting value(s) for integration. If it is a list or array, it must be the same length as - `default_variable ` (see `initializer - ` for details). + specifies starting value(s) for integration. If it is a list or array, it must be the same length as `variable + ` (see `initializer ` for details). params : Dict[param keyword: param value] : default None a `parameter dictionary ` that specifies the parameters for the @@ -4050,11 +4043,11 @@ class FitzHughNagumoIntegrator(IntegratorFunction): # ------------------------- initial_w : float, list or 1d array : default 0.0 specifies starting value for integration of dw/dt. If it is a list or array, it must be the same length as - `default_variable ` + `variable `. initial_v : float, list or 1d array : default 0.0 specifies starting value for integration of dv/dt. If it is a list or array, it must be the same length as - `default_variable ` + `variable ` time_step_size : float : default 0.1 specifies the time step size of numerical integration @@ -4144,11 +4137,11 @@ class FitzHughNagumoIntegrator(IntegratorFunction): # ------------------------- initial_w : float, list or 1d array : default 0.0 specifies starting value for integration of dw/dt. If it is a list or array, it must be the same length as - `default_variable ` + `variable ` initial_v : float, list or 1d array : default 0.0 specifies starting value for integration of dv/dt. If it is a list or array, it must be the same length as - `default_variable ` + `variable ` time_step_size : float : default 0.1 specifies the time step size of numerical integration diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index c36446c06b7..745fb34f0da 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -788,13 +788,13 @@ Several attributes are available for viewing the labels for the current value(s) of a Mechanism's InputPort(s) and OutputPort(s). - - The `label ` attribute of an InputPort or OutputPort returns the current label of - its value, if one exists, and its value otherwise. + - The `label ` attribute of an InputPort or OutputPort returns the current label of + its value, if one exists, and its numeric value otherwise. - - The `input_labels ` and `output_labels ` attributes of - Mechanisms return a list containing the labels corresponding to the value(s) of the InputPort(s) or - OutputPort(s) of the Mechanism, respectively. If the current value of a port does not have a corresponding - label, then its numeric value is used instead. + - The `labeled_input_values ` and `labeled_output_values + ` attributes of a Mechanism return lists containing the labels + corresponding to the value(s) of the InputPort(s) or OutputPort(s) of the Mechanism, respectively. If the + current value of a Port does not have a corresponding label, then its numeric value is reported instead. >>> output_labels_dict = {"red": [1, 0, 0], ... "green": [0, 1, 0], @@ -804,9 +804,9 @@ >>> C = pnl.Composition(pathways=[M]) >>> input_dictionary = {M: [[1, 0, 0]]} >>> results = C.run(inputs=input_dictionary) - >>> M.get_output_labels(C) + >>> M.labeled_output_values(C) ['red'] - >>> M.output_ports[0].get_label(C) + >>> M.output_ports[0].labeled_value(C) 'red' Labels may be used to visualize the input and outputs of Mechanisms in a Composition with the **show_structure** option @@ -1086,7 +1086,6 @@ import numpy as np import typecheck as tc -import psyneulink from psyneulink.core import llvm as pnlvm from psyneulink.core.components.component import Component from psyneulink.core.components.functions.function import FunctionOutputType @@ -1293,9 +1292,9 @@ class Mechanism_Base(Mechanism): in which each label (key) specifies a string associated with a value for the corresponding InputPort(s) of the Mechanism; see `Mechanism_Labels_Dicts` for additional details. - input_labels : list[str] - contains the labels corresponding to the value(s) of the InputPort(s) of the Mechanism. If the current value - of an InputPort does not have a corresponding label, then its numeric value is used instead. + labeled_input_values : list[str] + contains the labels corresponding to the current value(s) of the InputPort(s) of the Mechanism. If the + current value of an InputPort does not have a corresponding label, then its numeric value is used instead. external_input_ports : list[InputPort] list of the `input_ports ` for the Mechanism that are not designated as @@ -1303,9 +1302,11 @@ class Mechanism_Base(Mechanism): ` if the Mechanism is one of its `INPUT` `Nodes `. external_input_shape : List[List or 1d np.array] - list showing shapes of inputs expected for the Mechanism's `input_ports `. - Each item corresponds to an expected `path_afferent Projection ` and its shape the - expected `value ` of that `Projection`. + list of the `input_shape `\\s of the Mechanism's external `input_ports + ` (i.e., excluding any `InputPorts ` designated as `internal_only + `), that shows the shape of the inputs expected for the Mechanism. Each item + corresponds to an expected `path_afferent Projection `, and its shape is + the expected `value ` of that `Projection`. external_input_variables : List[List or 1d np.array] list of the `variable `\\s of the Mechanism's `external_input_ports @@ -1315,6 +1316,10 @@ class Mechanism_Base(Mechanism): list of the `value `\\s of the Mechanism's `external_input_ports `. + default_external_inputs : List[1d np.array] + list of the `default_input `\\s of the Mechanism's `external_input_ports + `. + COMMENT: target_labels_dict : dict contains entries that are either label:value pairs, or sub-dictionaries containing label:value pairs, @@ -1373,15 +1378,15 @@ class Mechanism_Base(Mechanism): the `value ` of that OutputPort (and its corresponding item in the the Mechanism's `output_values ` attribute). + labeled_output_values : list + contains the labels corresponding to the current value(s) of the OutputPort(s) of the Mechanism. If the + current value of an OutputPort does not have a corresponding label, then its numeric value is used instead. + output_labels_dict : dict contains entries that are either label:value pairs, or sub-dictionaries containing label:value pairs, in which each label (key) specifies a string associated with a value for the OutputPort(s) of the Mechanism; see `Mechanism_Labels_Dicts` for additional details. - output_labels : list - contains the labels corresponding to the value(s) of the OutputPort(s) of the Mechanism. If the current value - of an OutputPort does not have a corresponding label, then its numeric value is used instead. - standard_output_ports : list[dict] list of the dictionary specifications for `StandardOutputPorts ` that can be assigned as `OutputPorts `; subclasses may extend this list to include additional ones. @@ -3434,9 +3439,9 @@ def port_cell(port, include_function:bool=False, include_value:bool=False, use_l value='' if include_value: if use_label and not isinstance(port, ParameterPort): - value = f'
={port.label}' + value = f'
={port.labeled_value}' else: - value = f'
={port.value}' + value = f'
={port.labeled_value}' return f'{port.name}{function}{value}' @@ -3930,13 +3935,19 @@ def external_input_variables(self): except (TypeError, AttributeError): return None + @property + def default_external_inputs(self): + try: + return [input_port.default_input for input_port in self.input_ports if not input_port.internal_only] + except (TypeError, AttributeError): + return None + @property def default_external_input_variables(self): try: return [input_port.defaults.variable for input_port in self.input_ports if not input_port.internal_only] except (TypeError, AttributeError): return None - # MODIFIED 2/3/22 END @property def external_input_values(self): @@ -3953,7 +3964,7 @@ def default_external_input_values(self): return None @property - def input_labels(self): + def labeled_input_values(self): """ Returns a list with as many items as there are InputPorts of the Mechanism. Each list item represents the value of the corresponding InputPort, and is populated by a string label (from the input_labels_dict) when one @@ -3983,7 +3994,7 @@ def get_output_values(self, context=None): return [output_port.parameters.value.get(context) for output_port in self.output_ports] @property - def output_labels(self): + def labeled_output_values(self): """ Returns a list with as many items as there are OutputPorts of the Mechanism. Each list item represents the value of the corresponding OutputPort, and is populated by a string label (from the output_labels_dict) when diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 7fb9dbddd93..46146910553 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -234,7 +234,7 @@ .. _OptimizationControlMechanism_State_Features_Automatic_Assignment: - *Automatic assignment.* The **state_features** specify the inputs to the Composition assigned as the `agent_rep + *Automatic assignment.* The **state_features** specify the inputs to the Composition assigned as the `agent_rep ` when it is executed by the OptimizationControlMechanism to `evaluate ` its performance. The default is for the evaluation to use the same values received by the `agent_rep ` as its `external inputs @@ -248,17 +248,18 @@ .. _OptimizationControlMechanism_State_Features_Explicit_Specification: - *Explicit specificaiont.* The **state_features** argument can also be specified explicitly, using the formats + *Explicit specification.* The **state_features** argument can also be specified explicitly, using the formats described below. This is useful if values other than the `external inputs ` to - the `agent_rep ` Composition are to be used to evaluate it, to restrict - evaluation This allows values other than the `external inputs ` to the `agent_rep - ` Composition to be used to evaluate it; to restrict evaluation to a subset - of inputs (while others are held constant); and/or to assign specific functions to one or more `state_input_ports - ` that allow them to process inputs (e.g., modulate and/or integrate - them) before them as `state_feature_values state_feature_values ` - (see `below `). Note that assigning **state_features** - explicitly overrides their automatic assignment, so that all required values must be specified, and this must be - done accurate, as described below. + the `agent_rep ` Composition are to be used to evaluate it; to restrict + evaluation to a subset of inputs (while others are held constant); and/or to assign specific functions to one or + more `state_input_ports ` that allow them to process the inputs + (e.g., modulate and/or integrate them) before they are assigned as to `state_feature_values + ` (see `below + `). Note that assigning any **state_features** explicitly + overrides their automatic assignment, so that *all* required ones must be specified explicitly, as described below. + Any that are *not* specified will be assigned the value of their `default_variable ` + when the `agent_rep `\\'s `evaluate ` method is + executed. .. _OptimizationControlMechanism_State_Features_Shapes: @@ -290,10 +291,7 @@ .. _OptimizationControlMechanism_State_Features_Shadow_Inputs: - The specifications in the **state_features** argument are used to construct the `state_input_ports - `. As noted `above - `, specifying these explicitly overrides - their automatic construction. They can be specified using any of the following: + The **state_features** argument can be specified using any of the following formats: .. _Optimization_Control_Mechanism_State_Feature_Input_Dict: @@ -301,14 +299,18 @@ ` to the `agent_rep `, in which entries consist of a key specifying an `INPUT ` Node of `agent_rep `, and its value is the source of the input, that can be any of the forms - of individual input specifications listed `below - `. This is the most straightforward and reliable - way to specify **state_features**. The full format required for inputs to `agent_rep - ` can be seen using its `get_input_format ` - method. If only some `INPUT ` Nodes are specified, the remaining ones are assigned their - `default variable ` as input when the `agent_rep `\\'s - `evaluate ` method is called, irrespective of the input to the `agent_rep - ` during the last `TRIAL `. + of individual input specifications listed `below `. + This is the most straightforward and reliable way to specify **state_features**. The full format required for + inputs to `agent_rep ` can be seen using its `get_input_format + ` method. If any `INPUT ` Nodes are not specified or assigned None + as their value, their `default variable ` is used for their input when the + `agent_rep `\\'s `evaluate ` method is executed, + irrespective of the input to the `agent_rep ` during the last `TRIAL + `. If a nested Composition is specified (that is an `INPUT ` Node of `agent_rep + `), the value assigned to it is used for *all* of the `INPUT + ` Nodes for the nested Composition and any nested within it, at all levels of nesting. If one or + more `INPUT ` Nodes of a nested Composition (that are INPUT Nodes at all levels of nesting) are + specified, then any unspecified INPUT Nodes of the corresponding Compositions are assigned None as their values. .. _Optimization_Control_Mechanism_State_Feature_List_Inputs: @@ -895,9 +897,9 @@ to select one from its `search_space `), and evaluates the `net_outcome ` for that `control_allocation `. It does this by calling the OptimizationControlMechanism's `evaluate_agent_rep - ` method `num_estimates ` times, - each of which uses the `state_feature_values ` and - `control_allocation ` as the input to the `agent_rep + ` method `num_estimates ` + times, each of which uses the `state_feature_values ` + and `control_allocation ` as the input to the `agent_rep `\\'s `evaluate ` method, executing it for `num_trials_per_estimate ` trials for each estimate. The `state_feature_values ` and `control_allocation @@ -937,13 +939,13 @@ OptimizationControlMechanism's `evaluate_agent_rep ` method). The values of Components listed in the OptimizationControlMechanism's `random_variables ` attribute are randomized over those estimates. By default, -this includes all Components in the `agent_rep ` with random variables (listed -in its `random_variables ` attribute). However, if particular Components are specified -in the **random_variables** argument of the OptimizationControlMechanism's constructor, then randomization is -restricted to their values. Randomization over estimates can be further configured using the `initial_seed -` and `same_seed_for_all_allocations -` attributes. The results of all the estimates for a given -`control_allocation ` are aggregated by the `aggregation_function +this includes all Components in the `agent_rep ` with random variables +(listed in its `random_variables ` attribute). However, if particular Components +are specified in the **random_variables** argument of the OptimizationControlMechanism's constructor, then +randomization is restricted to their values. Randomization over estimates can be further configured using the +`initial_seed ` and `same_seed_for_all_allocations +` attributes. The results of all the estimates for a +given `control_allocation ` are aggregated by the `aggregation_function ` of the `OptimizationFunction` assigned to the OptimizationControlMechanism's `function `, and used to compute the `net_outcome ` over the estimates for that `control_allocation ` @@ -995,7 +997,6 @@ import copy import warnings from collections.abc import Iterable -from typing import Union import numpy as np import typecheck as tc @@ -1019,12 +1020,12 @@ from psyneulink.core.globals.defaults import defaultControlAllocation from psyneulink.core.globals.keywords import \ ALL, COMPOSITION, COMPOSITION_FUNCTION_APPROXIMATOR, CONCATENATE, DEFAULT_INPUT, DEFAULT_VARIABLE, EID_FROZEN, \ - FUNCTION, INTERNAL_ONLY, NAME, OPTIMIZATION_CONTROL_MECHANISM, OWNER_VALUE, PARAMS, PROJECTIONS, \ + FUNCTION, INTERNAL_ONLY, NAME, OPTIMIZATION_CONTROL_MECHANISM, NODE, OWNER_VALUE, PARAMS, PORT, PROJECTIONS, \ SHADOW_INPUTS, SHADOW_INPUT_NAME, VALUE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.sampleiterator import SampleIterator, SampleSpec -from psyneulink.core.globals.utilities import convert_to_list, convert_to_np_array, ContentAddressableList, is_numeric +from psyneulink.core.globals.utilities import convert_to_list, ContentAddressableList, is_numeric from psyneulink.core.llvm.debug import debug_env __all__ = [ @@ -1040,37 +1041,61 @@ NUM_ESTIMATES = 'num_estimates' def _state_feature_values_getter(owning_component=None, context=None): + """Return dict {agent_rep INPUT Node InputPort: value} suitable for **predicted_inputs** arg of evaluate method. + Only include entries for sources specified in **state_features**, corresponding to OCM's state_input_ports; + default input will be assigned for all other INPUT Node InputPorts (in composition._instantiate_input_dict()) + + """ + # If no state_input_ports return empty list if (not owning_component.num_state_input_ports): - return [] + return {} + + # Get sources specified in **state_features** + specified_state_features = [spec for spec in owning_component.state_feature_specs if spec is not None] + # Get INPUT Node InputPorts for sources specified in **state_features** + specified_INPUT_Node_InputPorts = [port for port, spec + in zip(owning_component._specified_INPUT_Node_InputPorts_in_order, + owning_component.state_feature_specs) + if spec is not None] + assert len(specified_state_features) == \ + len(specified_INPUT_Node_InputPorts) == \ + owning_component.num_state_input_ports + # If OptimizationControlMechanism is still under construction, use items from input_values as placemarkers - elif context.source == ContextFlags.CONSTRUCTOR: - return owning_component.input_values[owning_component.num_outcome_input_ports:] + if context.source == ContextFlags.CONSTRUCTOR: + return {k:v for k,v in zip(specified_INPUT_Node_InputPorts, + owning_component.input_values[owning_component.num_outcome_input_ports:])} + + # Construct state_feature_values dict + state_feature_values = {} + for i in range(owning_component.num_state_input_ports): + key = specified_INPUT_Node_InputPorts[i] + state_input_port = owning_component.state_input_ports[i] + spec = specified_state_features[i] + + # state_input_port not (yet) specified; default input will be assigned in _instantiate_input_dict() + if not isinstance(key, InputPort) or spec is None: + continue + + # FIX: 3/4/22 - RESTORE? + # if spec is None: + # # # FIX: ??TRY IGNORING RATHER THAN ASSIGNING, AS IT WILL BE ASSIGNED IN _instantiate_input_dict() + # # state_feature_values[state_input_port] = state_input_port.default_input_shape + # continue + + if is_numeric(spec): + state_feature_value = spec + elif state_input_port.parameters.value._get(context) is not None: + state_feature_value = state_input_port.parameters.value._get(context) + else: + state_feature_value = state_input_port.default_input_shape - # Otherwise, use current values of state_input_ports - state_input_port_values = [p.parameters.value.get(context) for p in owning_component.state_input_ports] + # state_feature_values[key] = convert_to_np_array(state_feature_value) + state_feature_values[key] = state_feature_value - if (not owning_component.state_feature_specs - or owning_component.num_state_input_ports == len(owning_component.state_feature_specs)): - # Automatically assigned state_features or full set of specs for all INPUT Nodes, - # so use values of state_input_ports (since there is one for every INPUT Node of agent_rep) - state_feature_values = state_input_port_values - else: - # Specified state_features for a subset of INPUT Nodes so use those - j = 0 - state_feature_values = [] - for node, spec in zip(owning_component._specified_input_nodes_in_order, - owning_component.state_feature_specs): - if spec is not None: - state_feature_values.append(state_input_port_values[j]) - j += 1 - else: - # FIX: 1/29/22 - HANDLE VARIBLE AS 1d vs 2d - # assert node.defaults.variable.ndim == 2 and len(node.defaults.variable)==1 - # state_feature_values.append(node.defaults.variable[0]) - state_feature_values.append(node.defaults.variable) + return state_feature_values - return convert_to_np_array(state_feature_values) class OptimizationControlMechanismError(Exception): def __init__(self, error_value): @@ -1552,7 +1577,8 @@ class Parameters(ControlMechanism.Parameters): agent_rep = Parameter(None, stateful=False, loggable=False, pnl_internal=True, structural=True) - state_feature_values = Parameter(None, getter=_state_feature_values_getter, user=False, pnl_internal=True) + state_feature_values = Parameter(None,getter=_state_feature_values_getter, + user=False, pnl_internal=True, read_only=True) # FIX: Should any of these be stateful? random_variables = ALL @@ -1586,24 +1612,22 @@ def _parse_state_feature_specs(self, state_features): @tc.typecheck def __init__(self, agent_rep=None, - state_features: tc.optional(tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort))) = - None, - state_feature_function: tc.optional(tc.optional(tc.any(dict, is_function_type))) = None, + state_features: tc.optional(tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort)))=None, + default_state_feature=None, + state_feature_function: tc.optional(tc.optional(tc.any(dict, is_function_type)))=None, function=None, - num_estimates = None, - random_variables = None, + num_estimates=None, + random_variables=None, initial_seed=None, same_seed_for_all_allocations=None, - num_trials_per_estimate = None, - search_function: tc.optional(tc.optional(tc.any(is_function_type))) = None, - search_termination_function: tc.optional(tc.optional(tc.any(is_function_type))) = None, + num_trials_per_estimate=None, + search_function: tc.optional(tc.optional(tc.any(is_function_type)))=None, + search_termination_function: tc.optional(tc.optional(tc.any(is_function_type)))=None, search_statefulness=None, context=None, **kwargs): """Implement OptimizationControlMechanism""" - from psyneulink.core.compositions.composition import Composition - # Legacy warnings and conversions for k in kwargs.copy(): if k == 'features': @@ -1773,7 +1797,7 @@ def _instantiate_input_ports(self, context=None): else: # Implement any specified state_features - state_input_ports_specs = self._parse_state_feature_specs() + state_input_ports_specs = self._parse_state_feature_specs(context) # Note: # if state_features were specified and agent_rep is a CompositionFunctionApproximator, # assume they are OK (no way to check their validity for agent_rep.evaluate() method, and skip assignment @@ -1798,31 +1822,13 @@ def _instantiate_input_ports(self, context=None): f"{port.name} should receive exactly one projection, " f"but it receives {len(port.path_afferents)} projections.") - def _get_agent_rep_input_nodes(self, comp=None, comp_as_node:Union[bool,ALL]=False): - """Return all input_nodes of agent_rep, including those for any Composition nested one level down. - If an INPUT Node is a Composition, and include_comp_as_node is: - - False, include the nested Composition's INPUT Nodes, but not the Composition - - True, include the nested Composition but not its INPUT Nodes - - ALL, include the nested Composition AND its INPUT Nodes - """ - from psyneulink.core.compositions.composition import Composition, NodeRole + def _get_agent_rep_input_receivers(self, comp=None, type=PORT, comp_as_node=False): if not self.agent_rep_type or self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR: return [None] comp = comp or self.agent_rep - _input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) - input_nodes = [] - for node in _input_nodes: - if isinstance(node, Composition): - if comp_as_node: - input_nodes.append(node) - if comp_as_node in {False, ALL}: - # FIX: DOESN'T THIS SEARCH RECURSIVELY? -- NEED TO TEST WITH > ONE LEVEL OF NESTING - input_nodes.extend(self._get_agent_rep_input_nodes(node)) - else: - input_nodes.append(node) - return input_nodes + return comp._get_input_receivers(comp=comp, type=type, comp_as_node=comp_as_node) - def _get_nodes_not_in_agent_rep(self, state_feature_specs): + def _get_specs_not_in_agent_rep(self, state_feature_specs): from psyneulink.core.compositions.composition import Composition agent_rep_nodes = self.agent_rep._get_all_nodes() return [spec for spec in state_feature_specs @@ -1833,11 +1839,22 @@ def _get_nodes_not_in_agent_rep(self, state_feature_specs): def _validate_input_nodes(self, nodes, enforce=None): """Check that nodes are INPUT Nodes of agent_rep - Raise exception for non-INPUT Nodes if **enforce** is specified; else warn. + INPUT Nodes are those at the top level of agent_rep as well as those of any Compositions nested within it + that are themselves INPUT Nodes of their enclosing Composition. + Raise exception for non-INPUT Nodes if **enforce** is specified; otherwise just issue warning. """ + from psyneulink.core.compositions.composition import Composition + agent_rep_input_nodes = self._get_agent_rep_input_receivers(type=NODE,comp_as_node=ALL) + agent_rep_input_ports = self._get_agent_rep_input_receivers(type=PORT) + agent_rep_all_nodes = self.agent_rep._get_all_nodes() non_input_node_specs = [node for node in nodes - if node not in self._get_agent_rep_input_nodes(comp_as_node=True)] - non_agent_rep_node_specs = [node for node in nodes if node not in self.agent_rep._get_all_nodes()] + # if node not in self._get_agent_rep_input_receivers(type=NODE, comp_as_node=ALL)] + if ((isinstance(node, (Mechanism, Composition)) and node not in agent_rep_input_nodes) + or (isinstance(node, Port) and (not isinstance(node, InputPort) + or node not in agent_rep_input_ports)))] + non_agent_rep_node_specs = [node for node in nodes + if ((isinstance(node, (Mechanism, Composition)) and node not in agent_rep_all_nodes) or + (isinstance(node, Port) and node.owner not in agent_rep_all_nodes))] # Deal with Nodes that are in agent_rep but not INPUT Nodes if non_input_node_specs: @@ -1847,7 +1864,7 @@ def _validate_input_nodes(self, nodes, enforce=None): else: items_str = f"contains items ({items}) that are not INPUT Nodes" message = f"The '{STATE_FEATURES}' specified for '{self.name}' {items_str} " \ - f"of its {AGENT_REP} ('{self.agent_rep.name}'); only INPUT Nodes can be in a set " \ + f"within its {AGENT_REP} ('{self.agent_rep.name}'); only INPUT Nodes can be in a set " \ f"or used as keys in a dict used to specify '{STATE_FEATURES}'." if enforce: raise OptimizationControlMechanismError(message) @@ -1871,32 +1888,75 @@ def _validate_input_nodes(self, nodes, enforce=None): warnings.warn(message) # FIX: 1/29/22 - REFACTOR TO SUPPORT TUPLE AND InportPort SPECIFICATION DICT FOR MULT. PROJS. TO STATE_INPUT_PORT + # FIX: 2/25/22 - REFACTOR TO SUPPORT InputPort SPECIFICATIONS IN dict, set and list specs def _parse_state_feature_specs(self, context=None): - """Parse entries of state_features specifications into InputPort spec dictionaries. + """Parse entries of state_features specifications used to construct state_input_ports. Called from _instantiate_input_ports() - state_features specify sources of values assigned to state_feature_values, and passed to agent_rep.evaluate() - as the inputs to its INPUT Nodes (**predicted_inputs argument if agent_rep is a Composition; - **feature_values** argument if it is a CompositionFunctionApproximator. - Use each to create Projection(s) from specified source; these may be direct, or indirect by way of a CIM - if the source is in a nested Composition). - If the number of state_features specified is less than the number of agent_rep INPUT Nodes, only construct - state_input_ports for the INPUT Nodes specified (for a list spec, the first n INPUT Nodes listed in - agent_rep.nodes, where n is the length of the list spec); as a result, the remaining INPUT Nodes are - provided their default variables as input when agent_rep.evaluate() executes. - If shadowing is specified, set INTERNAL_ONLY to True in entry of params dict for state_input_port's InputPort - specification dictionary (so that inputs to Composition are not required if the specified state_feature - is for an INPUT Node). - If an INPUT Node is specified that is not (yet) in agent_rep, and/or a source is specified that is not yet - in the self.composition, warn and defer creating a state_input_port; final check is made, and error(s) - generated for unresolved specifications at run time. + Parse **state_features** arg of constructor for OptimizationControlMechanism, assigned to state_feature_specs. + + state_feature_specs lists sources of inputs to *all* INPUT Nodes of agent_rep, at all levels of nesting; there + is one entry for every INPUT Node in agent_rep, and every INPUT Node of any nested Composition that is + itself an INPUT Node at any level of nesting. + + Construct a state_input_port for every entry in state_feature_specs that is not None: + the value of those state_input_ports comprise the state_feature_values attribute, and are provided as the + input to the INPUT Nodes of agent_rep when its evaluate() method is executed (as the **predicted_inputs** + argument if agent_rep is a Composition, and the **feature_values** argument if it is a + CompositionFunctionApproximator); for INPUT; + for None entries in state_feature_specs, the corresponding INPUT Nodes are provided their + default_external_input_shape as their input when agent_rep.evaluate() executes. + + Projection(s) to state_input_ports from sources specified in state_feature_specs can be direct, + or indirect by way of a CIM if the source is in a nested Composition. Handle four formats: - - list: list of sources; must be listed in same order as INPUT Nodes of agent_rep to which they correspond; - - dict: keys are INPUT Nodes of agent_rep and values are corresponding sources; - - set: INPUT Nodes for which shadowing is implemented - - SHADOW_INPUTS dict: single entry with key='SHADOW_INPUTS" and values=list of sources (see above). + + - dict {INPUT Node: source or None, INPUT Node: source or None...}: + - every key must be an INPUT Node of agent_rep or an INPUT Node of a nested Composition within it that + is itself an INPUT Node of its enclosing Composition, at any level of nesting; + - if a Composition is specified as a key, construct a state_input_port for each InputPort of each of its + INPUT Nodes, and those of any Compositions nested within it at all levels of nesting, and assign the + the value of the dict entry as the source for all of them; + - for INPUT Nodes not specified or assigned None as their value, assign corresponding entries in + state_feature_specs as None, and don't construct a state_input_port for them; + - if only one or some of the INPUT Nodes of a nested Composition are specified, + for the remaining ones assign the corresponding entries in state_feature_specs as None, + and don't construct state_input_ports for them; + - list [source, None, source...]: specifies source specs for INPUT Nodes + - must be listed in same order as *expanded* list of agent_rep INPUT Nodes to which they correspond (i.e., + nested Compositions that are INPUT Nodes replaced by their INPUT Nodes, for all levels of nesting); + - if there are fewer sources listed than INPUT Nodes, assign None to the entries in state_feature_specs + corresponding to the remaining INPUT Nodes and don't construct state_input_ports for them; + - if there more sources listed than INPUT Nodes, leave the excess ones, and label them as + 'EXPECTED INPUT NODE n' for later resolution (see below). + + - set {INPUT Node, Input Node...}: specifies INPUT Nodes to be shadowed + - every item must be an INPUT Node of agent_rep or an INPUT Node of a nested Composition within it that + is itself an INPUT Node of its enclosing Composition, at any level of nesting; + - if a Composition is specified, construct a state_input_port for each of its INPUT Nodes, and those of + any Compositions nested within it at all levels of nesting, each of which shadows the input of the + corresponding INPUT Node (see _InputPort_Shadow_Inputs). + - if only one or some of the INPUT Nodes of a nested Composition are specified, don't state_input_ports + for the remaining ones, and assign them their default inputs in evaluate(). + + IMPLEMENTATION NOTE: this is a legacy format for consistency with generic specification of shadowing inputs + - SHADOW_INPUTS dict {"SHADOW_INPUTS":[shadowable input, None, shadowable input...]}: + - all items must be a Mechanism (or one of its InputPorts) that is an INPUT Node of agent_rep or + of a nested Composition within it that is itself an INPUT Node; + - must be listed in same order as *expanded* list of agent_rep INPUT Nodes to which they correspond + (see list format above); + - construct a state_input_port for each non-None spec, and assign it a Projection that shadows the spec. + (see _InputPort_Shadow_Inputs). + + If shadowing is specified for an INPUT Node, set INTERNAL_ONLY to True in entry of params dict in + specification dictionary for corresponding state_input_port (so that inputs to Composition are not + required if the specified source is itself an INPUT Node). + + If an INPUT Node is specified that is not (yet) in agent_rep, and/or a source is specified that is not yet + in self.composition, warn and defer creating a state_input_port; final check is made, and error(s) + generated for unresolved specifications at run time. Assign functions specified in **state_feature_function** to InputPorts for all state_features @@ -1905,10 +1965,9 @@ def _parse_state_feature_specs(self, context=None): from psyneulink.core.compositions.composition import Composition, NodeRole # Agent rep's input Nodes and their names - agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) - # The following are all "full" lists; that is, there is an entry corresponding to every INPUT node of agent_rep - # List of INPUT Nodes for which state_features are specified, ordered according to agent_rep.nodes - self._specified_input_nodes_in_order = [] + agent_rep_input_ports = self._get_agent_rep_input_receivers(type=PORT) + self._specified_INPUT_Node_InputPorts_in_order = [] + # List of assigned state_feature_function (vs. user provided specs) self._state_feature_functions = [] @@ -1923,7 +1982,7 @@ def _parse_state_feature_specs(self, context=None): f"({self.state_feature_specs}).") # agent_rep has not yet been (fully) constructed - if not agent_rep_input_nodes and self.agent_rep_type is COMPOSITION: + if not agent_rep_input_ports and self.agent_rep_type is COMPOSITION: if (isinstance(self.state_feature_specs, set) or isinstance(self.state_feature_specs, dict) and SHADOW_INPUTS not in self.state_feature_specs): # Dict and set specs reference Nodes of agent_rep, and so must that must be constructed first @@ -1939,15 +1998,6 @@ def _parse_state_feature_specs(self, context=None): f"same as the order of the corresponding INPUT Nodes for '{self.agent_rep.name}' once " f"they are added, or unexpected results may occur. It is safer to assign all Nodes to " f"the {AGENT_REP} of a controller before specifying its '{STATE_FEATURES}'.") - else: - # # FIX: 1/16/22 - MAY BE A PROBLEM IF SET OR DICT HAS ENTRIES FOR INPUT NODES OF NESTED COMP THAT IS AN INPUT NODE - # FIX: 1/18/22 - ADD TEST FOR THIS WARNING TO test_ocm_state_feature_specs_and_warnings_and_errors: too_many_inputs - if len(self.state_feature_specs) < len(agent_rep_input_nodes): - warnings.warn(f"There are fewer '{STATE_FEATURES}' specified for '{self.name}' than the number of " - f"INPUT Nodes of its {AGENT_REP} ('{self.agent_rep.name}'); the remaining inputs will be " - f"assigned default values when '{self.agent_rep.name}`s 'evaluate' method is executed. " - f"If this is not the desired configuration, use its get_inputs_format() method to see " - f"the format for all of its inputs.") # HELPER METHODS ------------------------------------------------------------------------------------------ @@ -1960,42 +2010,56 @@ def expand_nested_input_comp_to_input_nodes(comp): input_nodes.append(node) return input_nodes - def get_inputs_for_nested_comp(comp): - # FIX: 1/18/22 - NEEDS TO BE MODIFIED TO RETURN TUPLE IF > INPUT NODE, ONCE THAT CAN BE HANDLED BY LIST SPEC - return comp.get_nodes_by_role(NodeRole.INPUT) - # PARSE SPECS ------------------------------------------------------------------------------------------ # Generate parallel lists of INPUT Nodes and corresponding feature specs (for sources of inputs) def _parse_specs(state_feature_specs, spec_str="list"): - """Validate and parse specs into Port references and construct state_features dict + """Validate and parse INPUT Node specs assigned to construct state_feature_specs Validate number and identity of specs relative to agent_rep INPUT Nodes. - Assign {node: spec} entries to state_features dict + Assign spec for every INPUT Mechanism (nested) within agent_rep (i.e., for all nested Compositions) + as entries in state_feature_specs Return names for use as input_port_names in main body of method """ - parsed_feature_specs = [] if self.agent_rep_type == COMPOSITION: - if len(state_feature_specs) > len(agent_rep_input_nodes): - nodes_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" - for spec in self._get_nodes_not_in_agent_rep(state_feature_specs)] - if nodes_not_in_agent_rep: - node_str = ", ".join(nodes_not_in_agent_rep) + + # FIX: 3/4/22 - THESE SEEM DUPLICATIVE OF _validate_state_features; JUST CALL THAT HERE? + # ALSO, WARNING IS TRIGGERED IF MECHANIMS RATHER THAN ITS INPUT_PORTS ARE SPEC'D + # Too FEW specs for number of agent_rep receivers + if len(self.state_feature_specs) < len(agent_rep_input_ports): + warnings.warn(f"There are fewer '{STATE_FEATURES}' specified for '{self.name}' than the number " + f"of {InputPort.__name__}'s for all of the INPUT Nodes of its {AGENT_REP} " + f"('{self.agent_rep.name}'); the remaining inputs will be assigned default values " + f"when '{self.agent_rep.name}`s 'evaluate' method is executed. If this is not the " + f"desired behavior, use its get_inputs_format() method to see the format for its " + f"inputs.") + + # Too MANY specs for number of agent_rep receivers + if len(state_feature_specs) > len(agent_rep_input_ports): + # specs_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" + # for spec in self._get_specs_not_in_agent_rep(state_feature_specs)] + specs_not_in_agent_rep = [f"'{spec.name if isinstance(spec,(Mechanism, Composition)) else spec.owner.name}'" + for spec in self._get_specs_not_in_agent_rep(user_specs)] + + if specs_not_in_agent_rep: + spec_str = ", ".join(specs_not_in_agent_rep) warnings.warn( - f"The number of '{STATE_FEATURES}' specified for {self.name} " - f"({len(state_feature_specs)}) is more than the number of INPUT Nodes " - f"({len(agent_rep_input_nodes)}) of the Composition assigned as its {AGENT_REP} " - f"('{self.agent_rep.name}'), which includes the following that " - f"are not in '{self.agent_rep.name}': {node_str}. Executing {self.name} before the " - f"additional Node(s) are added as INPUT Nodes will generate an error.") + f"The '{STATE_FEATURES}' specified for {self.name} is associated with a number of " + f"{InputPort.__name__}s ({len(state_feature_specs)}) that is greater than for the " + f"{InputPort.__name__}s of the INPUT Nodes ({len(agent_rep_input_ports)}) for the " + f"Composition assigned as its {AGENT_REP} ('{self.agent_rep.name}'), which includes " + f"the following that are not (yet) in '{self.agent_rep.name}': {spec_str}. Executing " + f"{self.name} before the additional item(s) are added as (part of) INPUT Nodes will " + f"generate an error.") else: warnings.warn( - f"The number of '{STATE_FEATURES}' specified for {self.name} " - f"({len(state_feature_specs)}) is more than the number of INPUT Nodes " - f"({len(agent_rep_input_nodes)}) of the Composition assigned as its {AGENT_REP} " - f"('{self.agent_rep.name}'). Executing {self.name} before the " - f"additional Nodes are added as INPUT Nodes will generate an error.") + f"The '{STATE_FEATURES}' specified for {self.name} is associated with a number of " + f"{InputPort.__name__}s ({len(state_feature_specs)}) that is greater than for the " + f"{InputPort.__name__}s of the INPUT Nodes ({len(agent_rep_input_ports)}) for the " + f"Composition assigned as its {AGENT_REP} ('{self.agent_rep.name}'). Executing " + f"{self.name} before the additional item(s) are added as (part of) INPUT Nodes will " + f"generate an error.") # Nested Compositions not allowed to be specified in a list spec nested_comps = [node for node in state_feature_specs if isinstance(node, Composition)] @@ -2008,30 +2072,30 @@ def _parse_specs(state_feature_specs, spec_str="list"): f"shadowed.") spec_names = [] num_specs = len(state_feature_specs) - num_nodes = len(agent_rep_input_nodes) - self._num_state_feature_specs = max(num_specs, num_nodes) + num_ports = len(agent_rep_input_ports) + self._num_state_feature_specs = max(num_specs, num_ports) for i in range(self._num_state_feature_specs): - # NODE & NODE_NAME + # PORT & PORT_NAME # (and specs for CFA and any nodes not yet in agent_rep) spec_name = None state_feature_fct = None - if i < num_nodes: - # Node should be in agent_rep, so use that to be sure - if self.agent_rep_type == COMPOSITION: - node = agent_rep_input_nodes[i] - # Assign spec as node for CompositionFunctionApproximator + if self.agent_rep_type == COMPOSITION: + if i < num_ports: + # Node should be in agent_rep, so use that to be sure + if self.agent_rep_type == COMPOSITION: + node = agent_rep_input_ports[i] + node_name = node.full_name else: + # Node not (yet) in agent_rep, so "DEFERRED n" as node name spec = state_feature_specs[i] - node = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner - node_name = node.name + node = None + node_name = f'DEFFERED {str(i-num_ports)}' + # Assign spec as node for CompositionFunctionApproximator else: - # FIX 2/18/22 - FALSELY ASSUMES THAT SPEC IN LIST IS FOR AN INPUT NODE RATHER THAN JUST ITS SOURCE - # SHOULD INSTEAD USE A PLACEMARKER, AND ASSIGN ONCE IT IS THERE IN - # _update_state_input_ports_for_controller() ?AND _update_state_features_dict()? - # Node not (yet) in agent_rep, so uses its node name spec = state_feature_specs[i] - node = None - node_name = f'DEFFERED {str(i-num_nodes)}' + node = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner + node_name = f"FEATURE {i} FOR {self.agent_rep.name}" + # SPEC # Assign specs # Only process specs for which there are already INPUT Nodes in agent_rep @@ -2064,77 +2128,129 @@ def _parse_specs(state_feature_specs, spec_str="list"): parsed_feature_specs.append(spec) self._state_feature_functions.append(state_feature_fct) - self._specified_input_nodes_in_order.append(node) + self._specified_INPUT_Node_InputPorts_in_order.append(node) spec_names.append(spec_name) self.parameters.state_feature_specs.set(parsed_feature_specs, override=True) return spec_names or [] user_specs = self.parameters.state_feature_specs.spec - # user_specs = self.state_feature_specs - # LIST spec - # Treat as source specs: - # - construct a regular dict using INPUT Nodes as keys and specs as values - if isinstance(user_specs, list): - input_port_names = _parse_specs(user_specs) - - # DICT spec - elif isinstance(user_specs, dict): - # SHADOW_INPUTS dict spec - if SHADOW_INPUTS in user_specs: - # Set not allowed as SHADOW_INPUTS spec + # LIST OR SHADOW_INPUTS DICT: source specs + # Source specs but not INPUT Nodes specified; spec is either: + # - list: [spec, spec...] + # - SHADOW_INPUTS dict (with list spec as its only entry): {SHADOW_INPUTS: {[spec, spec...]}} + # Treat specs as sources of input to INPUT Nodes of agent_rep (in corresponding order): + # Call _parse_specs to construct a regular dict using INPUT Nodes as keys and specs as values + if isinstance(user_specs, list) or (isinstance(user_specs, dict) and SHADOW_INPUTS in user_specs): + if isinstance(user_specs, list): + specs = user_specs + spec_str = 'list' + else: + # SHADOW_INPUTS spec: if isinstance(user_specs[SHADOW_INPUTS], set): - # Catch here to provide context-relevant error message + # Set not allowed as SHADOW_INPUTS spec; catch here to provide context-relevant error message raise OptimizationControlMechanismError( f"The '{STATE_FEATURES}' argument for '{self.name}' uses a set in a '{SHADOW_INPUTS.upper()}' " f"dict; this must be a single item or list of specifications in the order of the INPUT Nodes" f"of its '{AGENT_REP}' ({self.agent_rep.name}) to which they correspond." ) - input_port_names = _parse_specs(user_specs[SHADOW_INPUTS], f"{SHADOW_INPUTS.upper()} dict") - - # User {node:spec} dict spec - else: - specified_input_nodes = user_specs.keys() - self._validate_input_nodes(specified_input_nodes) - nodes = self._get_agent_rep_input_nodes(comp_as_node=True) - # Get specs in order of agent_rep INPUT Nodes, with None assigned to any unspecified INPUT Nodes - # as well as to any not in agent_rep at end which are placed at the end of the list - nodes.extend([node for node in specified_input_nodes if node not in nodes]) - specs = [user_specs[node] if node in specified_input_nodes else None for node in nodes] - # Get parsed specs and names (don't care about nodes since those are specified by keys - input_port_names = _parse_specs(specs) - - # SET spec - # Treat as specification of INPUT Nodes to be shadowed: - # - construct an InputPort dict with SHADOW_INPUTS as its key, and specs in a list as its value - elif isinstance(user_specs, set): - # All nodes must be INPUT nodes of agent_rep, that are to be shadowed, - self._validate_input_nodes(user_specs) - # specs = [node if node in state_feature_specs else None for node in agent_rep_input_nodes] - # FIX: 1/29/22 - - # THIS IS DANGEROUS -- SHOULD REPLACE ONCE TUPLE FORMAT IS IMPLEMENTED OR USE InputPort SPECIF DICT - # IT WORKS FOR NESTED COMPS WITH A SIGNLE INPUT NODE OF THEIR OWN - # BUT IF A NESTED COMP HAS MORE THAN ONE INPUT NODE, THIS WILL RETURN MORE THAN ONE NODE IN PLACE OF - # THE NESTED COMP AND THUS GET OUT OF ALIGNMENT WITH NUMBER OF INPUT NODES FOR AGENT_REP - # ONCE FIXED, EXTEND FOR USE WITH COMP AS SPEC IN LIST AND DICT FORMATS - # Replace any nested Comps that are INPUT Nodes of agent_comp with their INPUT Nodes so they are shadowed - all_nested_input_nodes = [] - for node in user_specs: - if isinstance(node, Composition): - all_nested_input_nodes.extend(get_inputs_for_nested_comp(node)) + # All specifications in list specified for SHADOW_INPUTS must be shadowable + # (i.e., either an INPUT Node or the InputPort of one) or None; + # note: if spec is not in agent_rep, might be added later, + # so defer dealing with that until runtime. + bad_specs = [spec for spec in user_specs[SHADOW_INPUTS] + if ( # spec is not an InputPort + ((isinstance(spec, Port) and not isinstance(spec, InputPort)) + # spec is an InputPort of a Node in agent_rep but not one of its INPUT Nodes + or (isinstance(spec, InputPort) + and spec.owner in self.agent_rep._get_all_nodes() + and spec.owner not in self._get_agent_rep_input_receivers(type=NODE, + comp_as_node=ALL)) + ) + and spec is not None)] + if bad_specs: + bad_spec_names = [f"'{item.owner.name}'" if hasattr(item, 'owner') + else f"'{item.name}'" for item in bad_specs] + raise OptimizationControlMechanismError( + f"The '{STATE_FEATURES}' argument for '{self.name}' has one or more items in the list " + f"specified for '{SHADOW_INPUTS.upper()}' ({', '.join([name for name in bad_spec_names])}) " + f"that are not (part of) any INPUT Nodes of its '{AGENT_REP}' ('{self.agent_rep.name}')." ) + + specs = user_specs[SHADOW_INPUTS] + spec_str = f"{SHADOW_INPUTS.upper()} dict" + input_port_names = _parse_specs(specs, spec_str=spec_str) + + # FIX: 2/25/22 - ?ITEMS IN set ARE SHADOWED, BUT UNSPECIFIED ITEMS IN SET AND DICT ARE ASSIGNED DEFAULT VALUES + # SET OR DICT: specification by INPUT Nodes + # INPUT Nodes of agent_rep specified in either a: + # - set, without source specs: {node, node...} + # - dict, with source specs for each: {node: spec, node: spec...} + # Call _parse_specs to convert to or flesh out dict with INPUT Nodes as keys and any specs as values + # adding any unspecified INPUT Nodes of agent_rep and assiging default values for any unspecified values + elif isinstance(user_specs, (set, dict)): + + # FIX: 3/4/22 REINSTATE & MOVE TO ABOVE?? + self._validate_input_nodes(set(user_specs)) + + # FIX: MOVE TO _validate_input_nodes + # Validate user_specs + internal_input_port_specs = [f"'{spec.full_name}'" for spec in user_specs + if isinstance(spec, InputPort) and spec.internal_only] + if internal_input_port_specs: + raise OptimizationControlMechanismError( + f"The following {InputPort.__name__}s specified in the '{STATE_FEATURES}' arg for {self.name} " + f"do not receive any external inputs and thus cannot be assigned '{STATE_FEATURES}': " + f"{', '.join(internal_input_port_specs)}.") + non_input_port_specs = [f"'{spec.full_name}'" for spec in user_specs + if isinstance(spec, Port) and not isinstance(spec, InputPort)] + if non_input_port_specs: + raise OptimizationControlMechanismError( + f"The following {Port.__name__}s specified in the '{STATE_FEATURES}' arg for {self.name} " + f"are not {InputPort.__name__} and thus cannot be assigned '{STATE_FEATURES}': " + f"{', '.join(non_input_port_specs)}.") + + expanded_specified_ports = [] + expanded_dict_with_ports = {} + dict_format = isinstance(user_specs, dict) + + # Expand any Nodes specified in user_specs set or keys of dict to corresponding InputPorts + for spec in user_specs: + # Expand any specified Compositions into corresponding InputPorts for all INPUT Nodes (including nested) + if isinstance(spec, Composition): + ports = self._get_agent_rep_input_receivers(spec) + # Expand any specified Mechanisms into corresponding InputPorts except any internal ones + elif isinstance(spec, Mechanism): + ports = [input_port for input_port in spec.input_ports if not input_port.internal_only] else: - all_nested_input_nodes.append(node) - # Get specs ordered by agent_rep INPUT Nodes, with any not in agent_rep at end and None for any not included - agent_rep_nodes = self._get_agent_rep_input_nodes() - nodes = [node if node in all_nested_input_nodes else None for node in agent_rep_nodes] - nodes.extend([node for node in all_nested_input_nodes if node not in agent_rep_nodes]) - input_port_names = _parse_specs(nodes) + ports = [spec] + expanded_specified_ports.extend(ports) + if dict_format: + # Assign values specified in user_specs dict to corresponding InputPorts + expanded_dict_with_ports.update({port:user_specs[spec] for port in ports}) + + # # Get specified ports in order of agent_rep INPUT Nodes, with None assigned to any unspecified InputPorts + all_specified_ports = [port if port in expanded_specified_ports else None for port in agent_rep_input_ports] + # Get any not found anywhere (including nested) in agent_rep, which are placed at the end of list + all_specified_ports.extend([port for port in expanded_specified_ports if port not in agent_rep_input_ports]) + + if isinstance(user_specs, set): + # Just pass ports; _parse_specs assigns shadowing projections for them and None to all unspecified ones + specs = all_specified_ports + else: + # Pass values from user_spec dict to be parsed; + # corresponding ports are safely in all_specified_ports + # unspecified ports are assigned None per requirements of list format + specs = [expanded_dict_with_ports[port] if port is not None and port in all_specified_ports else None + for port in all_specified_ports] + + input_port_names = _parse_specs(specs) # CONSTRUCT InputPort SPECS ----------------------------------------------------------------------------- state_input_port_specs = [] for i in range(self._num_state_feature_specs): - # Note: state_feature_specs have now been parsed (user's specs are in parameters.state_feature_specs.spec) + # Note: state_feature_specs have now been parsed (user's specs are in parameters.state_feature_specs.spec); + # state_feature_specs correspond to all InputPorts of agent_rep's INPUT Nodes (including nested ones) spec = self.state_feature_specs[i] if spec is None: continue @@ -2153,10 +2269,10 @@ def _parse_specs(state_feature_specs, spec_str="list"): # FIX: 11/29/21: MOVE THIS TO _parse_shadow_inputs # (ADD ARG TO THAT FOR DOING SO, OR RESTRICT TO InputPorts IN GENERAL) if len(spec.input_ports)!=1: - raise OptimizationControlMechanismError(f"A Mechanism ({spec.name}) is specified in the " - f"'{STATE_FEATURES}' arg for {self.name} that has " - f"more than one InputPort; a specific one or subset " - f"of them must be specified.") + raise OptimizationControlMechanismError( + f"A Mechanism ({spec.name}) is specified to be shadowed in the '{STATE_FEATURES}' arg " + f"for '{self.name}', but it has more than one {InputPort.__name__}; a specific one of its " + f"{InputPort.__name__}s must be specified to be shadowed.") spec = spec.input_port else: spec = spec.output_port @@ -2222,8 +2338,8 @@ def _parse_state_feature_function(self, feature_function): return feature_function def _update_state_features_dict(self): - agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=True) - specified_input_nodes = self._specified_input_nodes_in_order + agent_rep_input_ports = self._get_agent_rep_input_receivers() + specified_input_ports = self._specified_INPUT_Node_InputPorts_in_order for i, port in enumerate(self.state_input_ports): # Get value (need first, to determine whether it belongs to a nested Comp, for assigning key) @@ -2232,15 +2348,15 @@ def _update_state_features_dict(self): if (isinstance(feature, Component) and feature.owner in [n[0] for n in self.agent_rep._get_nested_nodes()]): node = feature.owner - elif specified_input_nodes[i]: - node = specified_input_nodes[i] - elif i < len(agent_rep_input_nodes): - node = specified_input_nodes[i] = agent_rep_input_nodes[i] + elif specified_input_ports[i]: + node = specified_input_ports[i] + elif i < len(agent_rep_input_ports): + node = specified_input_ports[i] = agent_rep_input_ports[i] else: node = None if not (isinstance(node, str) and 'DEFERRED' in node): continue - if feature.owner not in self._get_agent_rep_input_nodes(comp_as_node=ALL): + if feature.owner not in self._get_agent_rep_input_receivers(comp_as_node=ALL): # Don't add to dict, will be dealt with or raise an error at run time continue self.state_feature_specs[i] = feature @@ -2285,17 +2401,11 @@ def _update_state_input_ports_for_controller(self, context=None): # Restrict validation and any further instantiation of state_input_ports # until run time, when the Composition is expected to be fully constructed if context._execution_phase == ContextFlags.PREPARING: - # MODIFIED 1/30/22 NEW: # FIX: 1/30/22 - NEEDS TO EXECUTE ON UPDATES WITHOUT RUN, # BUT MANAGE ERRORS WRT TO _validate_state_features self._update_state_features_dict() - # MODIFIED 1/30/22 END - self._validate_state_features() - # MODIFIED 1/30/22 OLD: + self._validate_state_features(context) return - # # MODIFIED 1/30/22 NEW: - # return True - # MODIFIED 1/30/22 END elif not self.state_input_ports: # agent_rep is Composition, but no state_features have been specified, @@ -2304,13 +2414,12 @@ def _update_state_input_ports_for_controller(self, context=None): # Get list of nodes with any nested Comps that are INPUT Nodes replaced with their respective INPUT Nodes # (as those are what need to be shadowed) shadowed_input_ports = [] - for node in self._get_agent_rep_input_nodes(comp_as_node=False): - for input_port in node.input_ports: - if input_port.internal_only: - continue - # if isinstance(input_port.owner, CompositionInterfaceMechanism): - # input_port = input_port. - shadowed_input_ports.append(input_port) + for port in self._get_agent_rep_input_receivers(): + if port.internal_only: + continue + # if isinstance(input_port.owner, CompositionInterfaceMechanism): + # input_port = input_port. + shadowed_input_ports.append(port) # Instantiate state_input_ports local_context = Context(source=ContextFlags.METHOD) @@ -2338,12 +2447,12 @@ def _update_state_input_ports_for_controller(self, context=None): # Assign OptimizationControlMechanism attributes self.state_input_ports.data = state_input_ports self._num_state_feature_specs = len(self.state_input_ports) - self._specified_input_nodes_in_order = self._get_agent_rep_input_nodes(comp_as_node=False) + self._specified_INPUT_Node_InputPorts_in_order = self._get_agent_rep_input_receivers() self.parameters.state_feature_specs.set([input_port.shadow_inputs for input_port in self.state_input_ports], override=True) return True - def _validate_state_features(self): + def _validate_state_features(self, context): """Validate that state_features are legal and consistent with agent_rep. Called by _update_state_input_ports_for_controller, @@ -2358,6 +2467,7 @@ def _validate_state_features(self): - state_features are compatible with input format for agent_rep Composition """ + # FIX: 3/4/22 - DO user_specs HAVE TO BE RE-VALIDATED? ?IS IT BECAUSE THEY MAY REFER TO NEWLY ADDED NODES? from psyneulink.core.compositions.composition import \ Composition, CompositionInterfaceMechanism, CompositionError, RunError, NodeRole @@ -2379,7 +2489,7 @@ def _validate_state_features(self): input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) if len(state_feature_specs) > len(input_nodes): nodes_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" - for spec in self._get_nodes_not_in_agent_rep(state_feature_specs)] + for spec in self._get_specs_not_in_agent_rep(state_feature_specs)] missing_nodes_str = (f", that includes the following: {', '.join(nodes_not_in_agent_rep)} " f"missing from {self.agent_rep.name}" if nodes_not_in_agent_rep else '') @@ -2391,16 +2501,14 @@ def _validate_state_features(self): for i, spec in enumerate(state_feature_specs): input_dict[input_nodes[i]] = spec state_features = state_feature_specs - elif isinstance(state_feature_specs, dict): - # If user dict is specified, check that keys are legal INPUT nodes: - self._validate_input_nodes(state_feature_specs.keys(), enforce=True) - # If dict is specified, get values for checks below - state_features = list(state_feature_specs.values()) - elif isinstance(state_feature_specs, set): - # If user dict is specified, check that keys are legal INPUT nodes: - self._validate_input_nodes(state_feature_specs, enforce=True) - # If dict is specified, get values for checks below - state_features = list(state_feature_specs) + elif isinstance(state_feature_specs, (set, dict)): + # If set or dict is specified, check that items of set or keys of dict are legal INPUT nodes: + self._validate_input_nodes(set(state_feature_specs), enforce=True) + if isinstance(state_feature_specs, dict): + # If dict is specified, get values for checks below + state_features = list(state_feature_specs.values()) + else: + state_features = list(state_feature_specs) # Include agent rep in error messages if it is not the same as self.composition self_has_state_features_str = f"'{self.name}' has '{STATE_FEATURES}' specified " @@ -2436,7 +2544,7 @@ def _validate_state_features(self): invalid_state_features = [input_port for input_port in self.state_input_ports if (input_port.shadow_inputs and not (input_port.shadow_inputs.owner - in self._get_agent_rep_input_nodes()) + in self._get_agent_rep_input_receivers()) and (isinstance(input_port.shadow_inputs.owner, CompositionInterfaceMechanism) and not (input_port.shadow_inputs.owner.composition in @@ -2448,21 +2556,18 @@ def _validate_state_features(self): # # FOLLOWING IS FOR DEBUGGING: (TO SEE CODING ERRORS DIRECTLY) ----------------------- # print("****** DEBUGGING CODE STILL IN OCM -- REMOVE FOR PROPER TESTING ************") - # inputs = self.agent_rep._build_predicted_inputs_dict(None, self) - # inputs_dict, num_inputs = self.agent_rep._parse_input_dict(inputs) - # # END DEBUGGING --------------------------------------------------------------------- + # inputs_dict, num_inputs = self.agent_rep._parse_input_dict(self.parameters.state_feature_values._get(context)) + # # END DEBUGGING --------------------------------------------------------------------- # Ensure state_features are compatible with input format for agent_rep Composition try: - # FIX: 1/10/22 - ?USE self.agent_rep.external_input_values FOR CHECK? - # Call these to check for errors in constructing inputs dict - inputs = self.agent_rep._build_predicted_inputs_dict(None, self) - self.agent_rep._parse_input_dict(inputs) - except RunError as error: + # Call this to check for errors in constructing inputs dict + self.agent_rep._parse_input_dict(self.parameters.state_feature_values._get(context)) + except (RunError, CompositionError) as error: raise OptimizationControlMechanismError( f"The '{STATE_FEATURES}' argument has been specified for '{self.name}' that is using a " - f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but " - f"they are not compatible with the inputs required by its 'agent_rep': '{error.error_value}' " + f"{Composition.componentType} ('{self.agent_rep.name}') as its agent_rep, but some of the " + f"specifications are not compatible with the inputs required by its 'agent_rep': '{error.error_value}' " f"Use the get_inputs_format() method of '{self.agent_rep.name}' to see the required format, or " f"remove the specification of '{STATE_FEATURES}' from the constructor for {self.name} " f"to have them automatically assigned.") @@ -2632,7 +2737,7 @@ def _instantiate_attributes_after_function(self, context=None): self._initialize_composition_function_approximator(context) def _execute(self, variable=None, context=None, runtime_params=None): - """Find control_allocation that optimizes result of agent_rep.evaluate(). + """Find control_allocation that optimizes net_outcome of agent_rep.evaluate(). """ if self.is_initializing: @@ -3146,82 +3251,57 @@ def num_state_input_ports(self): @property def state_features(self): + """Return {InputPort: source} for all INPUT Nodes of agent_rep and sources specified by state_feature_specs. + """ + # FIX: 3/4/22 - REPLACE "EXPECTED" IN KEY WITH "DEFAULT VALUE FOR " + # for unspecified InputPorts if "needs_update_controller" is False + # - GET SOURCE OR SHADOWED SPEC self._update_state_features_dict() - agent_rep_input_nodes = self._get_agent_rep_input_nodes(comp_as_node=ALL) + agent_rep_input_ports = self._get_agent_rep_input_receivers() + # FIX: CONSTRAIN TO ONLY GET SOURCES: HERE OR BY ADDING ARG TO state_distal_sources_and_destinations_dict() + sources = [source_tuple[0] if source_tuple[0] != DEFAULT_VARIABLE else value + for source_tuple, value in self.state_feature_sources.items()] + # FIX: USES SOURCES AS VALUES FOR DICT BELOW state_features_dict = {} # Use num_state_feature_specs here instead of num_state_input_ports as there may be some "null" (None) specs + j = 0 for i in range(self._num_state_feature_specs): - # Assign keys as INPUT Nodes of agent_rep - if self._specified_input_nodes_in_order[i] in agent_rep_input_nodes: - k = self._specified_input_nodes_in_order[i] + # Assign InputPorts of INPUT Nodes of agent_rep as keys + if self._specified_INPUT_Node_InputPorts_in_order[i] in agent_rep_input_ports: + key = self._specified_INPUT_Node_InputPorts_in_order[i] + else: + key = f"EXPECTED INPUT NODE {i} OF {self.agent_rep.name}" + if self.state_feature_specs[i] is not None: + state_features_dict[key] = sources[j] + j += 1 else: - k = f"EXPECTED INPUT NODE {i} OF {self.agent_rep.name}" - state_features_dict[k] = self.state_feature_specs[i] + state_features_dict[key] = None + return state_features_dict @property def state_feature_sources(self): - """Dict with {INPUT Node: source} entries for sources specified in **state_features** arg of constructor.""" - # FIX: 1/30/22: - # FIX: SUPPORT tuple SPECIFICATION FOR sources - # FIX: REDUCE ALL MECHANISM SOURCES TO Port SPECS - # FIX: ??REFACTOR TO CONSTRUCT THE FOLLOWING: - # state_feature_specs_dict: {INPUT Node: spec} - # state_features: {INPUT Node: source} - if isinstance(self.state_feature_specs, dict) and SHADOW_INPUTS in self.state_feature_specs: - # SHADOW_INPUTS dict - state_feature_specs = self.state_feature_specs[SHADOW_INPUTS] - else: - # list and set specs - state_feature_specs = self.state_feature_specs - sources = [source_tuple[0] if source_tuple[0] != DEFAULT_VARIABLE else value - for source_tuple, value in self.state_distal_sources_and_destinations_dict.items()] - return {k:v for k,v in zip(self.state_features, sources)} - - def _state(self, context=None): - """Get context-specific state_feature and control_allocation values""" - # Use self.state_feature_values Parameter if state_features specified; else use state_input_port values - state_feat_vals = self.parameters.state_feature_values.get(context) - state_feature_values = state_feat_vals if len(state_feat_vals) else self.state_input_ports.values - # FIX: 1/30/22: USE CONTEXT TO GET control_allocations IF THAT IS NOT ALREADY DONE ON THAT ATTRIBUTE - return [v.tolist() for v in state_feature_values] + self.control_allocation.tolist() - - @property - def state(self): - """Array that is concatenation of state_feature_values and control_allocations""" - # Use self.state_feature_values Parameter if state_features specified; else use state_input_port values - return self.state_feature_values + self.control_allocation.tolist() - - @property - def state_distal_sources_and_destinations_dict(self): - """Return dict with (Port, Node, Composition, index) tuples as keys and corresponding state[index] as values. - Initial entries are for sources of the state_feature_values (i.e., distal afferents for state_input_ports) - and subsequent entries are for destination parameters modulated by the OptimizationControlMechanism's - ControlSignals (i.e., distal efferents of its ControlProjections). - Note: the index is required, since a state_input_port may have more than one afferent Projection - (that is, a state_feature_value may be determined by Projections from more than one source), - and a ControlSignal may have more than one ControlProjection (that is, a given element of the - control_allocation may apply to more than one Parameter). However, for state_input_ports that shadow - a Node[InputPort], only that Node[InputPort] is listed in state_dict even if the Node[InputPort] being - shadowed has more than one afferent Projection (this is because it is the value of the Node[InputPort] - (after it has processed the value of its afferent Projections) that determines the input to the - state_input_port. - """ - + """Dict with {InputPort: source} for all INPUT Nodes of agent_rep, and sources in **state_feature_specs.""" state_dict = {} - - # FIX: 1/30/22 - RECONCILE AND DOCUMENT MULTIPLE POSSIBLE SOURCES/DESTINATIONS FOR - # - THIS DICT AND STATE_FEATURES DICTS, WHICH MAY HAVE MORE THAN ONE ENTRY PER INPUT_NODE - # - VS. STATE_FEATURE_VALUES AND STATE_FEATURE_SPECS_DICT (WHICH HAVE ONLY ONE ENTRY PER INPUT_NODE - # Get sources for state_feature_values of state: + # FIX: 3/4/22 - THIS NEEDS TO HANDLE BOTH state_input_ports BUT ALSO state_feature_values FOR WHICH THERE ARE NO INPUTPORTS + specified_state_features = [spec for spec in self.state_feature_specs if spec is not None] for state_index, port in enumerate(self.state_input_ports): if not port.path_afferents: if port.default_input is DEFAULT_VARIABLE: + # MODIFIED 3/4/22 OLD: source_port = DEFAULT_VARIABLE + # # MODIFIED 3/4/22 NEW: + # if self.state_feature_specs[state_index] is not None: + # source_port = self.state_feature_specs[state_index] + # else: + # source_port = DEFAULT_VARIABLE + # MODIFIED 3/4/22 END node = None comp = None else: - continue + source_port = specified_state_features[state_index] + node = None + comp = None else: get_info_method = self.composition._get_source # MODIFIED 1/8/22: ONLY ONE PROJECTION PER STATE FEATURE @@ -3234,15 +3314,44 @@ def state_distal_sources_and_destinations_dict(self): get_info_method = composition._get_destination source_port, node, comp = get_info_method(port.path_afferents[0]) state_dict.update({(source_port, node, comp, state_index):self.state[state_index]}) - state_index += 1 + return state_dict + + @property + def control_signal_destinations(self): + state_dict = {} + state_index = self.num_state_input_ports # Get recipients of control_allocations values of state: for ctl_index, control_signal in enumerate(self.control_signals): for projection in control_signal.efferents: port, node, comp = self.composition._get_destination(projection) state_dict.update({(port, node, comp, state_index + ctl_index):self.state[state_index + ctl_index]}) - return state_dict + @property + def state(self): + """Array that is concatenation of state_feature_values and control_allocations""" + # Use self.state_feature_values Parameter if state_features specified; else use state_input_port values + return list(self.state_feature_values.values()) + list(self.control_allocation) + + @property + def state_distal_sources_and_destinations_dict(self): + """Return dict with (Port, Node, Composition, index) tuples as keys and corresponding state[index] as values. + Initial entries are for sources of the state_feature_values (i.e., distal afferents for state_input_ports) + and subsequent entries are for destination parameters modulated by the OptimizationControlMechanism's + ControlSignals (i.e., distal efferents of its ControlProjections). + Note: the index is required, since a state_input_port may have more than one afferent Projection + (that is, a state_feature_value may be determined by Projections from more than one source), + and a ControlSignal may have more than one ControlProjection (that is, a given element of the + control_allocation may apply to more than one Parameter). However, for state_input_ports that shadow + a Node[InputPort], only that Node[InputPort] is listed in state_dict even if the Node[InputPort] being + shadowed has more than one afferent Projection (this is because it is the value of the Node[InputPort] + (after it has processed the value of its afferent Projections) that determines the input to the + state_input_port. + """ + sources_and_destinations = self.state_feature_sources + sources_and_destinations.update(self.control_signal_destinations) + return sources_and_destinations + @property def _model_spec_parameter_blacklist(self): # default_variable is hidden in constructor arguments, diff --git a/psyneulink/core/components/mechanisms/processing/objectivemechanism.py b/psyneulink/core/components/mechanisms/processing/objectivemechanism.py index 1ce5d403310..c81cdf8adc7 100644 --- a/psyneulink/core/components/mechanisms/processing/objectivemechanism.py +++ b/psyneulink/core/components/mechanisms/processing/objectivemechanism.py @@ -72,7 +72,7 @@ used to specify attributes of the InputPort and/or MappingProjection(s) to it, that the ObjectiveMechanism creates to monitor the specified OutputPort. In general, the `value ` of each specified OutputPort determines the format of the `variable ` of the InputPort that is created for it by the ObjectiveMechanism. -However, this can be overridden using the ObjectiveMechanism's `default_variable ` +However, this can be overridden using the ObjectiveMechanism's `default_variable ` or `size ` attributes (see `Mechanism InputPort specification `), or by specifying a Projection from the OutputPort to the InputPort (see `Input Source Specification `). If an item in the diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 2f8415c32eb..0771c92971d 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -521,14 +521,13 @@ """ import collections import inspect -import itertools import numbers import warnings import numpy as np import typecheck as tc -from psyneulink.core.components.component import Component, DefaultsFlexibility +from psyneulink.core.components.component import DefaultsFlexibility from psyneulink.core.components.functions.function import Function from psyneulink.core.components.functions.nonstateful.combinationfunctions import CombinationFunction, LinearCombination from psyneulink.core.components.ports.outputport import OutputPort @@ -673,10 +672,14 @@ class InputPort(Port_Base): ` attribute is automtically assigned True. This is so that if the `Mechanism` to which the InputPort belongs is assigned to a `Composition`, it is not treated as an `ORIGIN ` `Node ` of that Composition (and automatically assigned a Projection - from its `input_CIM `. + from its `input_CIM `. + + input_shape : 1d array + shows the shape of the input to the InputPort; that is, the shape of the `value ` + expected for any `path_afferent Projections `. function : Function - If it is a `CombinationFunction`, it combines the `values ` of the `PathwayProjections + if it is a `CombinationFunction`, it combines the `values ` of the `PathwayProjections ` (e.g., `MappingProjections `) received by the InputPort (listed in its `path_afferents ` attribute), under the possible influence of `GatingProjections ` received by the InputPort (listed in its `mod_afferents ` @@ -1350,10 +1353,6 @@ def socket_width(self): def socket_template(self): return np.zeros(self.socket_width) - @property - def label(self): - return self.get_label() - def get_label(self, context=None): try: label_dictionary = self.owner.input_labels_dict @@ -1372,6 +1371,11 @@ def _input_shape_template(self): assert False, f"PROGRAM ERROR: Missing or unrecognized 'changes_shape' attribute for " \ f"('{self.function.name}') of '{self.name}'." + @property + def input_shape(self): + """Alias for default_input_shape_template""" + return self.default_input_shape + @property def default_input_shape(self): if self._input_shape_template == VARIABLE: diff --git a/psyneulink/core/components/ports/outputport.py b/psyneulink/core/components/ports/outputport.py index 6a6cb5f5056..ed86defa1bc 100644 --- a/psyneulink/core/components/ports/outputport.py +++ b/psyneulink/core/components/ports/outputport.py @@ -615,11 +615,12 @@ """ import copy -import numpy as np -import typecheck as tc import types import warnings +import numpy as np +import typecheck as tc + from psyneulink.core.components.component import Component, ComponentError from psyneulink.core.components.functions.function import Function from psyneulink.core.components.ports.port import Port_Base, _instantiate_port_list, port_type_keywords @@ -627,7 +628,8 @@ from psyneulink.core.globals.keywords import \ ALL, ASSIGN, CALCULATE, CONTEXT, CONTROL_SIGNAL, FUNCTION, GATING_SIGNAL, INDEX, INPUT_PORT, INPUT_PORTS, \ MAPPING_PROJECTION, MECHANISM_VALUE, NAME, OUTPUT_PORT, OUTPUT_PORTS, OUTPUT_PORT_PARAMS, \ - OWNER_VALUE, PARAMS, PARAMS_DICT, PROJECTION, PROJECTIONS, RECEIVER, REFERENCE_VALUE, STANDARD_OUTPUT_PORTS, PORT, VALUE, VARIABLE, \ + OWNER_VALUE, PARAMS, PARAMS_DICT, PROJECTION, PROJECTIONS, RECEIVER, REFERENCE_VALUE, STANDARD_OUTPUT_PORTS, PORT, \ + VALUE, VARIABLE, \ output_port_spec_to_parameter_name, INPUT_PORT_VARIABLES from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set @@ -1283,10 +1285,6 @@ def efferents(self): def calculate(self): return self.assign - @property - def label(self): - return self.get_label() - def get_label(self, context=None): try: label_dictionary = self.owner.output_labels_dict diff --git a/psyneulink/core/components/ports/parameterport.py b/psyneulink/core/components/ports/parameterport.py index e0d882eda06..455a4ca4742 100644 --- a/psyneulink/core/components/ports/parameterport.py +++ b/psyneulink/core/components/ports/parameterport.py @@ -971,6 +971,9 @@ 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 self.source._get(context) + def get_label(self, context): + raise ParameterPortError(f"{ParameterPort.__name__}s do not have labels.") + @property def pathway_projections(self): raise ParameterPortError("PROGRAM ERROR: Attempt to access {} for {}; {}s do not have {}s". @@ -981,6 +984,7 @@ def pathway_projections(self, value): raise ParameterPortError("PROGRAM ERROR: Attempt to assign {} to {}; {}s cannot accept {}s". format(PATHWAY_PROJECTION, self.name, PARAMETER_PORT, PATHWAY_PROJECTION)) + def _instantiate_parameter_ports(owner, function=None, context=None): """Call _instantiate_parameter_port for all modulable parameters to instantiate ParameterPorts for them diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index 84a5b8b9627..3f0370c4662 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -771,29 +771,28 @@ def test_multiple_modulatory_projections_with_mech_and_port_Name_specs(self): import sys import types import warnings - -from collections.abc import Iterable from collections import defaultdict +from collections.abc import Iterable import numpy as np import typecheck as tc from psyneulink.core import llvm as pnlvm from psyneulink.core.components.component import ComponentError, DefaultsFlexibility, component_keywords -from psyneulink.core.components.functions.nonstateful.combinationfunctions import CombinationFunction, LinearCombination from psyneulink.core.components.functions.function import Function, get_param_value_for_keyword, is_function_type +from psyneulink.core.components.functions.nonstateful.combinationfunctions import CombinationFunction, LinearCombination from psyneulink.core.components.functions.nonstateful.transferfunctions import Linear from psyneulink.core.components.shellclasses import Mechanism, Projection, Port from psyneulink.core.globals.context import ContextFlags, handle_external_context from psyneulink.core.globals.keywords import \ - ADDITIVE, ADDITIVE_PARAM, AUTO_ASSIGN_MATRIX, CONTEXT, CONTROL, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, \ - DEFAULT_INPUT, DEFAULT_VARIABLE, DEFERRED_INITIALIZATION, DISABLE,\ + ADDITIVE, ADDITIVE_PARAM, AUTO_ASSIGN_MATRIX, CONTEXT, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, \ + DEFAULT_INPUT, DEFAULT_VARIABLE, DEFERRED_INITIALIZATION, DISABLE, \ EXPONENT, FUNCTION, FUNCTION_PARAMS, GATING_PROJECTION_PARAMS, GATING_SIGNAL_SPECS, INPUT_PORTS, \ LEARNING_PROJECTION_PARAMS, LEARNING_SIGNAL_SPECS, \ MATRIX, MECHANISM, MODULATORY_PROJECTION, MODULATORY_PROJECTIONS, MODULATORY_SIGNAL, \ MULTIPLICATIVE, MULTIPLICATIVE_PARAM, \ NAME, OUTPUT_PORTS, OVERRIDE, OWNER, \ - PATH_AFFERENTS, PARAMETER_PORTS, PARAMS, PATHWAY_PROJECTIONS, PREFS_ARG, \ + PARAMETER_PORTS, PARAMS, PATHWAY_PROJECTIONS, PREFS_ARG, \ PROJECTION_DIRECTION, PROJECTIONS, PROJECTION_PARAMS, PROJECTION_TYPE, \ RECEIVER, REFERENCE_VALUE, REFERENCE_VALUE_NAME, SENDER, STANDARD_OUTPUT_PORTS, \ PORT, PORT_COMPONENT_CATEGORY, PORT_CONTEXT, Port_Name, port_params, PORT_PREFS, PORT_TYPE, port_value, \ @@ -2235,6 +2234,15 @@ def find_label_value_match(self, key, labels_dict, context=None): return label return self.parameters.value.get(context) + @property + def labeled_value(self): + return self.get_label() + + @property + def value_label(self): + """Alias of labeled_value""" + return self.labeled_value + @property def owner(self): return self._owner diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 4c2894762c1..eb3f7784d5f 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -1074,18 +1074,18 @@ - `Composition_Input_Dictionary` - `Composition_Programmatic_Inputs` -All `methods of executing a Composition require specification of an **inputs** -argument (and a **targets** argument for `learn ` method), which designates the values assigned -to the `INPUT ` `(and, for learning, the `TARGET `) Nodes ` +All `methods of executing ` a Composition require specification of an **inputs** +argument (and a **targets** argument for the `learn ` method), which designates the values assigned +to the `INPUT ` (and, for learning, the `TARGET `) `Nodes ` of the Composition. These are provided to the Composition each time it is executed; that is, for each `TRIAL `. A `TRIAL ` is defined as the opportunity for every Node in the Composition to execute the current set of inputs. The inputs for each `TRIAL ` can be specified using an `input dictionary `; for the `run ` and `learn ` methods, they can also be specified `programmatically `. Irrespective of format, the same number of inputs must be specified for every `INPUT` Node, unless only one value is specified for a Node (in which -case that value is provided as the input to that Node for every `TRIAL `\\s executed). If the -**inputs** argument is not specified for the `run ` or `execute ` methods, -the `default_variable ` for each `INPUT` Node is used as its input on `TRIAL `. +case that value is repeated as the input to that Node for every `TRIAL `\\s executed). If the +**inputs** argument is not specified for the `run ` or `execute ` methods, the +`default_variable ` for each `INPUT` Node is used as its input on `TRIAL `. If it is not specified for the `learn ` method, an error is generated unless its **targets** argument is specified (see `below `). The Composition's `get_input_format() ` method can be used to show an example for how inputs should be formatted for the @@ -1118,16 +1118,19 @@ (see `above `). The format required can also be seen using the `get_input_format() ` method. +.. _Composition_Input_Internal_Only: + .. note:: - Most Mechanisms have only a single `InputPort`, and thus require only a single input to be specified for them - for each `TRIAL `. However some Mechanisms have more than one InputPort (for example, a - `ComparatorMechanism`), in which case an input must be specified for each InputPort of that Mechanism. Conversely, - some Mechanisms have input_ports that are marked as `internal_only ` (for example, - the `input_port ` for a `RecurrentTransferMechanism`, if its `has_recurrent_input_port - ` is True), in which case no input should be specified for - that input_port. Similar considerations extend to the `external_input_ports ` - of a `nested Composition `, based on the Mechanisms (and/or additionally nested Compositions) - that comprise its set of `INPUT` `Nodes `. + Most Mechanisms have only a single `InputPort`, and thus require only a single input to be specified for + them for each `TRIAL `. However some Mechanisms have more than one InputPort (for example, + a `ComparatorMechanism`), in which case inputs can be specified for some or all of them (see `below + `). Conversely, some Mechanisms have InputPorts that are designated + as `internal_only ` (for example, the `input_port ` for a + `RecurrentTransferMechanism`, if its `has_recurrent_input_port ` + attribute is True), in which case no input should be specified for those input_ports. Similar considerations + extend to the `external_input_ports ` of a `nested Composition + `, based on the Mechanisms (and/or additionally nested Compositions) that comprise its set of + `INPUT` `Nodes `. The factors above determine the format of each entry in an `inputs dictionary `, or the return value of the function or generator used for `programmatic specification ` of @@ -1139,40 +1142,146 @@ *Input Dictionary* ================== +.. _Composition_Input_Dictionary_Entries: + The simplest way to specificy inputs (including targets for learning) is using a dict, in which each entry specifies -the inputs to a given `INPUT` `Node `. The key of each entry is a Node, and the value is a list of -the inputs to that Node, one for each `TRIAL ` to be executed (i.e., the i-th item of the list -represents the input to the Node on `TRIAL ` i). The same number of input values must be specified -in each entry, unless only a single input value is specified is in an entry, in which case that input is presented to -the corresonding Node in every `TRIAL `. - -.. _Composition_Execution_Input_Dict_Fig: - -.. figure:: _static/Composition_input_dict_spec.svg - :alt: Example input dict specification showing inputs specified for each Node and its InputPorts - - Example input dict specification, in which the first entry is for Mechanism ``a`` with one `InputPort` that takes - an array of length 2 as its input, and for which two `TRIAL `\\s worth of input are specified - (``[1.0, 2.0]`` and ``[3,0, 4.0]``); the second entry is for Mechanism ``b`` with two InputPorts, one of which - takes an array of length 1 as its input and the other an array of length 2, and for which two `TRIAL - `\\s worth of input are also specified (``[[1.0], [2.0, 3.0]]`` and ``[[4.0], [5.0, 6.0]]``); - and, finaly, a third entry is for Mechanism ``c`` with only one InputPort that takes an array of length 1 as its - input, and for which only one input is specified (``[1.0]``), which is therefore provided as the input to - Mechanism ``c`` on every `TRIAL `. - -The key for each entry of the dict can be a direct reference to the `Node `, or the name assigned -to one (i.e., its `name ` attribute). The value must an input that is compatible with the number of -`InputPorts ` that receive external input for that Node. These are listed in its ``external_input_ports`` -(`here ` if it is Mechanism, or `here ` if it -is a Composition). More specifically, the shape of the input value must be compatible with the shape of the Node's -`external_input_variables` attribute (`here ` if it is Mechanism, or `here -` if it is a Composition). While these are always 2d arrays, the number and size -of the items (corresponding to each InputPort) may vary; in some case shorthand notations are allowed, as illustrated -in the `examples ` below. +the inputs to a given `INPUT ` `Node `. The key for each entry of the dict is +either an `INPUT ` `Node ` or the `InputPort` of one, and the value is the input +to be provided to it for each `TRIAL ` of execution. A diciontary can have entries for *either* an +INPUT Node or one or more of its InputPorts, but *not both*. Entries can be for any `INPUT ` Node +(or the Inputport(s) of one) at any level of nesting within the Composition, so long it is nested under INPUT Nodes +at all levels of nesting (that is, an INPUT Node of a nested Composition can only be included if the nested Composition +is a INPUT Node of the Composition to which it belongs). Any INPUT Nodes for which no input is specified (that is, for +which there are no entries in the inputs dictionary) are assigned their `default_external_inputs +` on each `TRIAL ` of execution; similarly, if the dictionary +contains entries for some but not all of the InputPorts of a Node, the remaining InputPorts are assigned their +`default_input ` on each `TRIAL ` of execution. See below for additional +information concerning `entries for Nodes ` and `entries for InputPorts +`). + +*Input values*. The value of each entry is an ndarray or nested list containing the inputs to that Node or InputPort. +For Nodes, the value is a 3d array (or correspondingly nested list), in which the outermost items are 2d arrays +containing the 1d array of input values to each of the Node's InputPorts for a given `TRIAL `. For +entries specifying InputPorts, the value is a 2d array, containing 1d arrays with the input to the InputPort for each +`TRIAL `. A given entry can specify either a single `TRIAL `\\'s worth of input +(i.e., a single item in its outermost dimension), or inputs for every `TRIAL ` to be executed (in +which the i-th item represents the input to the `INPUT ` Node, or one of its InputPorts, on `TRIAL +` i). All entries that contain more than a single trial's worth of input must contain exactly the +same number of values -- i.e.,inputs for the same number of trials. For entries that contain a single input value, +that value will be provided repeatedly as the input to the specified Component for every `TRIAL ` +when the Composition is executed (as determined by the number of input values specified for other entries and/or the +**num_trials** argument of the Composition's `run ` method (see `number of trials +` above). + +.. _Composition_Input_Dictionary_Node_Entries: + +*Node entries*. The key must be an `INPUT ` `Node `of the Composition, or the name of +one (i.e., the str in its `name ` attribute), and the value must specify the input to *all* of its +InputPorts (other than those designated as `internal_only `; see `above +`) for one or all `TRIAL `\\s of execution. The values for each +`TRIAL ` must be compatible with each of the corresponding InputPorts (listed in the +`external_input_ports ` attribute of a Mechanism, and similarly in the +`external_input_ports ` attribute of a Composition). More specifically, +the shape of each item in the outer dimension (i.e., the input for each `TRIAL `, as described `above +`) must be compatible with the shape of the Node's +`external_input_shape ` attribute if it is Mechanism, and similarly the +`external_input_shape ` attribute of a Composition). While these are always 2d +arrays, the number and size of the 1d arrays within them (corresponding to each InputPort) may vary; in some case +shorthand notations are allowed, as illustrated in the `examples ` below. + + .. _Composition_Execution_Input_Dict_Fig: + + .. figure:: _static/Composition_input_dict_spec.svg + :alt: Example input dict specification showing inputs specified for each Node and its InputPorts + + Example input dict specification, in which the first entry is for Mechanism ``a`` with one `InputPort` that takes + an array of length 2 as its input, and for which two `TRIAL `\\s worth of input are specified + (``[1.0, 2.0]`` and ``[3,0, 4.0]``); the second entry is for Mechanism ``b`` with two InputPorts, one of which + takes an array of length 1 as its input and the other an array of length 2, and for which two `TRIAL + `\\s worth of input are also specified (``[[1.0], [2.0, 3.0]]`` and ``[[4.0], [5.0, 6.0]]``); + and, finaly, a third entry is for Mechanism ``c`` with only one InputPort that takes an array of length 1 as its + input, and for which only one input is specified (``[1.0]``), which is therefore provided as the input to + Mechanism ``c`` on every `TRIAL `. + +.. _Composition_Input_Dictionary_InputPort_Entries: + +*InputPort Entries*. The key must be an external `InputPort` (that is, one that is not designated as `internal_only +`; see `above `) for an `INPUT ` `Node +` of the Composition, or the `full_name ` of one, and the value must specify the +input for one or all `TRIAL `\\s of execution. Any or all of the InputPorts for an`INPUT ` +`Node ` can be specified, but an inputs dictionary cannot have specifications for both the Node +and any of its InputPorts. If the name of an InputPort is used as the key, its the str in its `full_name +` attribute must be used, to ensure disambiguation from any similarly named InputPorts of other +Nodes. Specifying InputPorts individually (instead of specifying all of them in a single entry for a Node) can be +if only some InputPorts should receive inputs, or the input for some needs to remain constant across `TRIAL +`\\s (by providing it with only one input value) while the input to others vary (i.e., by providing +input_values for every `TRIAL `). The value of each entry must be a 2d array or nested list +containing the input for either a single `TRIAL ` or all `TRIAL `\\s, each of which +must match the `input_shape ` of the InputPort. As with Nodes, if there are entries for some +but not all of a Node's InputPorts, the ones not specified are assigned their `default_input ` +values for every `TRIAL ` of execution. + +COMMENT: + Example of input dictionary show various ways of specifying inputs for Nodes and InputPorts:: + + >>> A = ComparatorMechanism(name='A') + >>> B = ProcessingMechanism(name='B', default_variable=[0,0,0]) + >>> inner_nested_comp = Composition(nodes=[A, B]) + + >>> C = ComparatorMechanism(name='C', size=3) + >>> nested_comp_1 = Composition(nodes=[C, inner_nested_comp]) + + >>> D = ComparatorMechanism(name='D', size=3) + >>> E = ComparatorMechanism(name='E', size=3) + >>> nested_comp_2 = Composition([D, E]) + + >>> F = ComparatorMechanism(name='F') + >>> G = ProcessingMechanism(name='G') + >>> nested_comp_3 = Composition([F, G]) + + >>> H = ComparatorMechanism(name='H') + >>> I = ProcessingMechanism(name='I') + >>> outer_comp = Composition(pathways=[nested_comp_1, nested_comp_2], nodes=[H, I, nested_comp_3]]) + + >>> input_dict = {A.input_ports['SAMPLE']:[[.5]], # Note: only a signle TRIAL of input is specified + ... A['TARGET']:[[1],[2]], # Note: name of InputPort is used as key + ... B:[[[3,4,5]]], # Note: only a signle TRIAL of input is specified + ... "C":[[[.5],[1]]], # Note: name of Node is used as key + ... D:[[[6,7,8]],[[9,10,11]]], + ... nested_comp_3: [[[12]],[[13]], # Note: full input nested Composition is provide + ... [[14]],[[15]]]} # for each TRIAL of execution + >>> outer_comp.get_input_format() + >>> outer_comp.external_input_shape + >>> outer_comp.get_external_input_ports + >>> outer_comp.run(inputs=inputs) + Add output here + + Show example of get_input_format() + Show example of get_external_inputs + + In this example, `ComparatorMechanism` ``A`` has two `InputPorts ` that are specified inidividually, + one of which (``SAMPLE``) has only one `TRIAL ` of input specified, while the other + (``TARGET``) has two `TRIAL `\\s of input specified; ``B`` has only one `TRIAL ` + specified, the length of which matches the shape of its `default_variable `; + + - Add show_graph() + - A & B are both INPUT Nodes of inner_nested_comp, which is an INPUT node of nested_comp_1, which is an INPUT Node + of outer_comp, so they count as INPUT Nodes + - D is an INPUT Node of nested_comp_2, but since nested_comp_2 is *not* an INPUT Node of outer_comp, it is not + D is not an INPUT Node of outer_comp and thus neiter D nor nested_comp_2 can be listed in the inputs_dict + - nested_comp_3 is an INPUT Node of outer_comp, so it (as shown in this example) or its INPUT Nodes (F, G) or their + InputPorts can be included as entries in the inputs dict for outer_comp. + - inputs for nested_comp_3 must contain all of the InputPorts for all of its InputNodes (F and G): two for F and + one for G; all have to have the same number of trials (here two) as each other and all other entries in the + inputs dictionary. + - H and I are both INPUT Nodes of outer_comp + - 'C' is specified by its name +COMMENT .. _Composition_Input_Labels: -In general, the value of inputs should be numeric arrays; however, some Mechanisms have an `input_labels_dict +*Input Labels*. In general, the value of inputs should be numeric arrays; however, some Mechanisms have an +`input_labels_dict ` that specifies a mapping from strings (labels) to numeric values, in which those strings can be used to specify inputs to that Mechanism (these are translated to their numeric values on execution). However, such labels are specific to a given Mechanism; use of strings as input to a Mechanism that does not have an @@ -1181,8 +1290,8 @@ .. _Composition_Target_Inputs: -For learning, inputs must also be specified for the `TARGET_MECHANISM ` of each -`learning Pathway ` in the Composition. This can be done in either the **inputs** +*Target Inputs for learning*. Inputs must also be specified for the `TARGET_MECHANISM ` +of each `learning Pathway ` in the Composition. This can be done in either the **inputs** argument or **targets** argument of the `learn ` method. If the **inputs** argument is used, it must include an entry for each `TARGET_MECHANISM `; if the **targets** argument is used, it must be assigned a dictionary containing entries in which the key is either an `OUTPUT_MECHANISM @@ -2013,8 +2122,8 @@ def input_function(env, result): >>> comp.run(inputs=input_dictionary) -Since the specification of the `default_variable ` for Mechanism ``a`` is a single array of -length 2, it is constructed with a single `InputPort` (see `Mechanism_InputPorts`) that takes an array of that +Since the specification of the `default_variable ` for Mechanism ``a`` is a single array +of length 2, it is constructed with a single `InputPort` (see `Mechanism_InputPorts`) that takes an array of that shape as its input; therefore, the input value specified for each `TRIAL ` is a length 2 array (``[1.0, 1.0]``). In contrast, since the `default_variable ` for Mechanism ``b`` is two length 1 arrays, so it is constructed with two InputPorts, each of which takes a length 1 array as its input; @@ -2046,7 +2155,7 @@ def input_function(env, result): ` which are excluded from its `external_input_ports ` and therefore its `external_input_variables `, and so should not receive an input value. The same considerations extend to the `external_input_ports ` - and `external_input_variabels ` of a Composition, based on the Mechanisms + and `external_input_variables ` of a Composition, based on the Mechanisms and/or `nested Compositions ` that comprise its `INPUT` Nodes. If num_trials is not in use, the number of inputs provided determines the number of `TRIAL `\\s in @@ -2605,6 +2714,7 @@ def input_function(env, result): from psyneulink.core import llvm as pnlvm from psyneulink.core.components.component import Component, ComponentsMeta +from psyneulink.core.components.functions.fitfunctions import make_likelihood_function from psyneulink.core.components.functions.function import is_function_type from psyneulink.core.components.functions.nonstateful.combinationfunctions import LinearCombination, \ PredictionErrorDeltaFunction @@ -2645,9 +2755,10 @@ def input_function(env, result): MATRIX, MATRIX_KEYWORD_VALUES, MAYBE, \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, \ MODEL_SPEC_ID_RECEIVER_MECH, MODEL_SPEC_ID_SENDER_MECH, \ - MONITOR, MONITOR_FOR_CONTROL, NAME, NESTED, NO_CLAMP, OBJECTIVE_MECHANISM, ONLINE, OUTCOME, \ + MONITOR, MONITOR_FOR_CONTROL, NAME, NESTED, NO_CLAMP, NODE, OBJECTIVE_MECHANISM, ONLINE, OUTCOME, \ OUTPUT, OUTPUT_CIM_NAME, OUTPUT_MECHANISM, OUTPUT_PORTS, OWNER_VALUE, \ - PARAMETER, PARAMETER_CIM_NAME, PROCESSING_PATHWAY, PROJECTION, PROJECTION_TYPE, PROJECTION_PARAMS, PULSE_CLAMP, \ + PARAMETER, PARAMETER_CIM_NAME, PORT, \ + PROCESSING_PATHWAY, PROJECTION, PROJECTION_TYPE, PROJECTION_PARAMS, PULSE_CLAMP, \ SAMPLE, SHADOW_INPUTS, SOFT_CLAMP, SSE, \ TARGET, TARGET_MECHANISM, TEXT, VARIABLE, WEIGHT, OWNER_MECH from psyneulink.core.globals.log import CompositionLog, LogCondition @@ -2668,7 +2779,6 @@ def input_function(env, result): from psyneulink.library.components.mechanisms.processing.transfer.recurrenttransfermechanism import \ RecurrentTransferMechanism from psyneulink.library.components.projections.pathway.autoassociativeprojection import AutoAssociativeProjection -from psyneulink.core.components.functions.fitfunctions import make_likelihood_function __all__ = [ 'Composition', 'CompositionError', 'CompositionRegistry', 'EdgeType', 'get_compositions', 'NodeRole' @@ -3393,13 +3503,25 @@ class Composition(Composition_Base, metaclass=ComponentsMeta): `, then from any `Nodes ` in the outer composition that project to the nested Composition (either itself, as a Node in the outer Composition, or to any of its own Nodes). - external_input_values : list[InputPort] + external_input_shape : list[InputPort] + a list of the `external_input_shape `\\s of all of the `InputPorts ` + of the Composition's `INPUT ` `Nodes ` (corresponding to the external + InputPots of its `input_CIM `); any input to the Composition must be compatible with the shape of + this, whether received from the **inputs** argument of one of the Composition's`execution methods + ` or, if it is a `nested Composition `, from the outer + Composition. + + external_input_variables : list[InputPort] a list of the values of associated with the `InputPorts ` listed in `external_input_ports `; any input to the Composition must be compatible with the shape of this, whether received from the **input_ports** argument of oneo f the Composition's`execution methods ` or, if it is a `nested Composition `, from the outer Composition. + external_input_values : list[InputPort] + a list of the values of associated with the `InputPorts ` listed in `external_input_ports + `. + output_CIM : `CompositionInterfaceMechanism` aggregates output values from the OUTPUT nodes of the Composition. If the Composition is nested, then the output_CIM and its OutputPorts serve as proxies for Composition itself in terms of efferent projections @@ -3650,6 +3772,7 @@ def __init__( self.needs_update_graph_processing = True # Tracks if the processing graph is current with the full graph self.needs_update_scheduler = True # Tracks if the scheduler needs to be regenerated self.needs_update_controller = True # Tracks if controller needs to update its state_input_ports + self.needs_determine_node_roles = False # Set in add_node and add_projection to insure update of NodeRoles self._need_check_for_unused_projections = True self.nodes_to_roles = collections.OrderedDict() @@ -3938,6 +4061,7 @@ def add_node(self, node, required_roles=None, context=None): # # Call _analyze_graph with ContextFlags.METHOD to avoid recursion # self._analyze_graph(context=Context(source=ContextFlags.METHOD)) # MODIFIED 1/27/22 END + self.needs_determine_node_roles = True def add_nodes(self, nodes, required_roles=None, context=None): """ @@ -4200,6 +4324,81 @@ def _get_input_nodes_by_CIM_input_order(self): return [{cim[0]:n for n, cim in self.input_CIM_ports.items()}[input_port].owner for input_port in self.input_CIM.input_ports] + def _get_input_receivers(self, + comp=None, + type:Union[PORT,NODE]=PORT, + comp_as_node:Union[bool,ALL]=False): + """Return all INPUT Nodes [or their InputPorts] of comp, [including those for any nested Compositions]. + If type is PORT, return all InputPorts for all INPUT Nodes, including for nested Compositions. + If type is NODE, return all INPUT Nodes, including for nested Compositions as determined by comp_as_node: + if an INPUT Node is a Composition, and comp_as_node is: + - False, include the nested Composition's INPUT Nodes, but not the Composition + - True, include the nested Composition but not its INPUT Nodes + - ALL, include the nested Composition AND its INPUT Nodes + """ + assert not (type == PORT and comp_as_node), f"PROGRAM ERROR: _get_input_receivers() can't be called " \ + f"for 'ports' and 'nodes' at the same time." + input_items = [] + _update_cim = False + + if comp.needs_determine_node_roles: + comp._determine_node_roles() + _update_cim = True + + if type==PORT: + # Return all InputPorts of all INPUT Nodes + _input_nodes = comp._get_nested_nodes_with_same_roles_at_all_levels(comp=comp, + include_roles=NodeRole.INPUT) + if _input_nodes: + for node in _input_nodes: + input_items.extend([input_port for input_port in node.input_ports if not input_port.internal_only]) + # Insure correct number of InputPorts have been identified (i.e., number of InputPorts on comp's input_CIM) + # MODIFIED 3/12/22 NEW: + if _update_cim: + context = Context() + self._determine_pathway_roles(context=context) + self._determine_pathway_roles(context) + self._create_CIM_ports(context) + _update_cim = False + # MODIFIED 3/12/22 OLD: + assert len(input_items) == len(comp.input_CIM_ports) + # MODIFIED 3/12/22 END + else: + # Return all INPUT Nodes + _input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) + for node in _input_nodes: + if isinstance(node, Composition): + if comp_as_node: + input_items.append(node) + if comp_as_node in {False, ALL}: + input_items.extend(self._get_input_receivers(comp=node, type=type, comp_as_node=comp_as_node)) + else: + input_items.append(node) + + return input_items + + def _get_external_cim_input_port(self, port:InputPort, outer_comp=None): + """Get input_CIM.input_port of outer(most) Composition that projects to port in nested Composition. + **port** must be an InputPort that receives a single path_afferent Projection from an input_CIM. + Search up nesting hierarchy to find the input_CIM of comp nested in outer_comp or of the outermost Composition. + Return tuple with: + input_CIM.input_port of the input_CIM of comp nested in outer_comp or of the outermost Composition + input_CIM.output_port corresponding to input_CIM.input_port (for ease of tracking Node to which it projects) + """ + assert len(port.path_afferents) == 1, \ + f"PROGRAM ERROR: _get_external_cim_input_ports called for {port} " \ + f"that has either no or more than one path_afferent projections." + input_CIM = port.path_afferents[0].sender.owner + assert isinstance(input_CIM, CompositionInterfaceMechanism), \ + f"PROGRAM ERROR: _get_external_cim_input_ports called for {port} that is not an INPUT Node " \ + f"(i.e., does not receive a path_afferent projection from an input_CIM of its enclosing Composition." + input_CIM_input_port = input_CIM.port_map[port][0] + if (outer_comp and input_CIM.composition in outer_comp.nodes) or not input_CIM_input_port.path_afferents: + # input_CIM_input_port belongs to outermost Composition, so return + return input_CIM.port_map[port] + # input_CIM_input_port belongs to a nested Composition, so continue to search up the nesting hierarchy + return self._get_external_cim_input_port(input_CIM_input_port, outer_comp) + def _get_nested_nodes(self, nested_nodes=NotImplemented, root_composition=NotImplemented, @@ -4715,6 +4914,8 @@ def _determine_node_roles(self, context=None): if self.controller is not None: self.nodes_to_roles[self.controller] = {NodeRole.CONTROLLER} + self.needs_determine_node_roles = False + def _set_node_roles(self, node, roles): self._clear_node_roles(node) for role in roles: @@ -5556,6 +5757,7 @@ def add_projection(self, # self.feedback_senders.add(sender_mechanism) # self.feedback_receivers.add(receiver_mechanism) + self.needs_determine_node_roles = True return projection def _add_projection(self, projection): @@ -8043,87 +8245,6 @@ def _check_nodes_initialization_status(self, context=None): f"or a composition nested within it." ) - def _build_predicted_inputs_dict(self, predicted_inputs, controller=None): - """Format predict_inputs from controller as input to evaluate method used to execute simulations of Composition. - - Get values of state_input_ports that receive projections from items providing relevant input (and any - processing of those values specified), and format as input_dict suitable for run() method (called in evaluate) - Deal with inputs for nodes in nested Compositions - """ - controller = controller or self.controller - # Use keys for inputs dict from OptimizationControlMechanism state_features if it is specified as a dict - # Note: these are always Mechanisms, including those that are INPUT Nodes in nested Compositions; - # this is so that if any INPUT Nodes of a nested Composition are unspecified, defaults can be assigned - input_dict_keys = list(controller.state_features.keys()) - inputs = {} - - no_predicted_input = (predicted_inputs is None or not len(predicted_inputs)) - if no_predicted_input: - warnings.warn(f"{self.name}.evaluate() called without any inputs specified; default values will be used") - - nested_nodes = dict(self._get_nested_nodes()) - - for i in range(controller.num_state_input_ports): - input_port = controller.state_input_ports[i] - if no_predicted_input: - predicted_input = input_port.default_input_shape - else: - predicted_input = predicted_inputs[i] - - # Shadow input specified - if hasattr(input_port, SHADOW_INPUTS) and input_port.shadow_inputs is not None: - shadow_input_owner = input_port.shadow_inputs.owner - if self._controller_initialization_status == ContextFlags.DEFERRED_INIT \ - and shadow_input_owner not in nested_nodes \ - and shadow_input_owner not in self.nodes: - continue - if shadow_input_owner in nested_nodes: - comp = nested_nodes[shadow_input_owner] - if comp in inputs: - inputs[comp]=np.concatenate([[predicted_input],inputs[comp][0]]) - else: - # FIX 1/9/22: CAN THIS BE REPLACED BY CALL TO _get_source? - def _get_enclosing_comp_for_node(input_port, comp): - """Get the Composition that is a node of self in which node nested in it. - - input_port is of node for which enclosing comp is being sought - - comp is one to which that node belongs - """ - # Get input_port for comp's CIM corresponding to - cim_input_port = comp.input_CIM.port_map[input_port][0] - # Outermost Composition, so return that - if not cim_input_port.path_afferents: - return comp - enclosing_comp = cim_input_port.path_afferents[0].sender.owner.composition - # Recursively search up through enclosing Compositions until one is a node of self - while comp not in self.nodes and comp is not self: - comp = _get_enclosing_comp_for_node(cim_input_port, enclosing_comp) - return comp - shadow_input_comp = nested_nodes[shadow_input_owner].input_CIM.composition - comp_for_input = _get_enclosing_comp_for_node(input_port.shadow_inputs, shadow_input_comp) - if comp_for_input in inputs: - # If node for nested comp is already in inputs dict, append to its input - inputs[comp_for_input][0].append(predicted_input) - else: - # Create entry in inputs dict for nested comp containing shadowed_input - inputs[comp_for_input] = [[predicted_input]] - else: - if isinstance(shadow_input_owner, CompositionInterfaceMechanism): - shadow_input_owner = shadow_input_owner.composition - # Use key from OptimizationControlMechanism state_features dict if specified, else the source node - key = input_dict_keys[i] if input_dict_keys else shadow_input_owner - inputs[key] = predicted_input - - # Regular input specified (i.e., projection from an OutputPort) - else: - # assert len(input_port.path_afferents) == 1 - if input_port.path_afferents: - source = input_port.path_afferents[0].sender.owner - # Use key from OptimizationControlMechanism state_features dict if specified, else the source node - key = input_dict_keys[i] if input_dict_keys else source - inputs[key] = predicted_input - - return inputs - def _get_total_cost_of_control_allocation(self, control_allocation, context, runtime_params): total_cost = 0. if control_allocation is not None: # using "is not None" in case the control allocation is 0. @@ -8189,8 +8310,8 @@ def evaluate( """Run Composition and compute `net_outcomes ` Runs the `Composition` in simulation mode (i.e., excluding its `controller `) - using the **predicted_input** and specified **control_allocation** for each run. The Composition is - run for ***num_trials**. + using the **predicted_input** (state_feature_values and specified **control_allocation** for each run. + The Composition is run for ***num_trials**. If **predicted_input** is not specified, and `block_simulate` is set to True, the `controller ` attempts to use the entire input set provided to the `run ` @@ -8225,8 +8346,7 @@ def evaluate( elif isinstance(input_spec, dict): inputs = input_spec else: - inputs = self._build_predicted_inputs_dict(predicted_input, - controller=controller) + inputs = predicted_input if hasattr(self, '_input_spec') and block_simulate and isgenerator(input_spec): warnings.warn(f"The evaluate method of {self.name} is attempting to use block simulation, but the " @@ -8492,23 +8612,27 @@ def _parse_function(self, inputs): num_trials = 1 return inputs, num_trials - def _validate_single_input(self, node, input): - """ - validates a single input for a single node. if the input is specified without an outer list (i.e. - Composition.run(inputs = [1, 1]) instead of Composition.run(inputs = [[1], [1]]), add an extra dimension - to the input + def _validate_single_input(self, receiver, input): + """Validate a single input for a single receiver. + If the input is specified without an outer list (i.e. Composition.run(inputs = [1, 1]) instead of + Composition.run(inputs = [[1], [1]]), add an extra dimension to the input Returns ------- `None` or `np.ndarray` or `list` : The input, with an added dimension if necessary, if the input is valid. `None` if the input is not valid. - """ - # Validate that a single input is properly formatted for a node. + + # Validate that a single input is properly formatted for a receiver. _input = [] - node_variable = node.external_input_shape - match_type = self._input_matches_variable(input, node_variable) + if isinstance(receiver, InputPort): + input_shape = receiver.default_input_shape + elif isinstance(receiver, Mechanism): + input_shape = receiver.external_input_shape + elif isinstance(receiver, Composition): + input_shape = receiver.input_CIM.external_input_shape + match_type = self._input_matches_variable(input, input_shape) if match_type == 'homogeneous': # np.atleast_2d will catch any single-input ports specified without an outer list _input = convert_to_np_array(input, 2) @@ -8541,35 +8665,52 @@ def _validate_input_shapes(self, inputs): input_lengths = set() inputs_to_duplicate = [] # loop through input dict - for node, stimulus in inputs.items(): - # see if the entire stimulus set provided is a valid input for the node (i.e. in the case of a call with a + for receiver, stimulus in inputs.items(): + # see if the entire stimulus set provided is a valid input for the receiver (i.e. in the case of a call with a # single trial of provided input) - node_input = self._validate_single_input(node, stimulus) - if node_input is not None: - node_input = [node_input] + _input = self._validate_single_input(receiver, stimulus) + if _input is not None: + _input = [_input] else: - # if node_input is None, it may mean there are multiple trials of input in the stimulus set, - # so in list comprehension below loop through and validate each individual input - node_input = [self._validate_single_input(node, single_trial_input) for single_trial_input in stimulus] + # if _input is None, it may mean there are multiple trials of input in the stimulus set, + # so in list comprehension below loop through and validate each individual input; + _input = [self._validate_single_input(receiver, single_trial_input) for single_trial_input in stimulus] # Look for any bad ones (for which _validate_single_input() returned None) and report if found - if True in [i is None for i in node_input]: - incompatible_stimulus = np.atleast_1d(np.array(stimulus[node_input.index(None)], dtype=object)) - correct_stimulus = np.atleast_1d(np.array(node.external_input_shape[node_input.index(None)], - dtype=object)) - node_name = node.name - err_msg = f"Input stimulus ({incompatible_stimulus}) for {node_name} is incompatible with " \ - f"the shape of its external input ({correct_stimulus})." + if any(i is None for i in _input): + if isinstance(receiver, InputPort): + receiver_shape = receiver.default_input_shape + receiver_name = receiver.full_name + elif isinstance(receiver, Mechanism): + receiver_shape = receiver.external_input_shape + receiver_name = receiver.name + elif isinstance(receiver, Composition): + receiver_shape = receiver.input_CIM.external_input_shape + receiver_name = receiver.name + # # MODIFIED 3/12/22 OLD: + # bad_stimulus = np.atleast_1d(np.squeeze(np.array(stimulus[_input.index(None)], dtype=object))) + # correct_stimulus = np.atleast_1d(np.array(receiver_shape[_input.index(None)], dtype=object)) + # err_msg = f"Input stimulus ({bad_stimulus}) for {receiver_name} is incompatible with " \ + # f"the shape of its external input ({correct_stimulus})." + # MODIFIED 3/12/22 NEW: + # FIX: MIS-REPORTS INCOMPATIBLITY AS BEING FOR SHAPE IF NUM TRIALS IS DIFFERENT FOR DIFF PORTS + # SHOULD BE HANDLED SAME AS FOR DIFFERNCE ACROSS NODES (PER BELOW) + receiver_shape = np.atleast_1d(np.squeeze(np.array(receiver_shape, dtype=object))) + bad_stimulus = [stim for stim, _inp in zip(stimulus, _input) if _inp is None] + bad_stimulus = np.atleast_1d(np.squeeze(np.array(bad_stimulus, dtype=object))) + err_msg = f"Input stimulus ({bad_stimulus}) for {receiver_name} is incompatible with " \ + f"the shape of its external input ({receiver_shape})." + # MODIFIED 3/12/22 END # 8/3/17 CW: I admit the error message implementation here is very hacky; # but it's at least not a hack for "functionality" but rather a hack for user clarity - if "KWTA" in str(type(node)): + if "KWTA" in str(type(receiver)): err_msg = err_msg + " For KWTA mechanisms, remember to append an array of zeros " \ "(or other values) to represent the outside stimulus for " \ "the inhibition InputPort, and for Compositions, put your inputs" raise RunError(err_msg) - _inputs[node] = node_input - input_length = len(node_input) + _inputs[receiver] = _input + input_length = len(_input) if input_length == 1: - inputs_to_duplicate.append(node) + inputs_to_duplicate.append(receiver) # track input lengths. stimulus sets of length 1 can be duplicated to match another stimulus set length. # there can be at maximum 1 other stimulus set length besides 1. input_lengths.add(input_length) @@ -8578,7 +8719,7 @@ def _validate_input_shapes(self, inputs): if len(input_lengths) > 1: raise CompositionError(f"The input dictionary for {self.name} contains input specifications of different " f"lengths ({input_lengths}). The same number of inputs must be provided for each " - f"node in a Composition.") + f"receiver in a Composition.") elif len(input_lengths) > 0: num_trials = list(input_lengths)[0] for mechanism in inputs_to_duplicate: @@ -8635,11 +8776,21 @@ def _flatten_nested_dicts(self, inputs): def _parse_names_in_inputs(self, inputs): names = [] + # Get keys that are names rather than Components for key in inputs: if isinstance(key, str): names.append(key) - named_nodes = [(node, node.name) for node in self.get_nodes_by_role(NodeRole.INPUT) if node.name in names] - for node, name in named_nodes: + # named_entries = [(node, node.name) for node in self.get_nodes_by_role(NodeRole.INPUT) if node.name in names] + named_entries = [] + for node in self.get_nodes_by_role(NodeRole.INPUT): + if node.name in names: + named_entries.append((node, node.name)) + else: + for port in node.input_ports: + if port.full_name in names: + named_entries.append((port, port.full_name)) + # Replace name with node itself in key + for node, name in named_entries: inputs[node] = inputs.pop(name) return inputs @@ -8749,32 +8900,142 @@ def _parse_input_dict(self, inputs, context=None): def _instantiate_input_dict(self, inputs): """Implement dict with all INPUT Nodes of Composition as keys and their assigned inputs or defaults as values + **inputs** can contain specifications for inputs to InputPorts, Mechanisms and/or nested Compositions, + that can be at any level of nesting within self. Validate that all Nodes included in input dict are INPUT nodes of Composition. - If any input nodes of Composition are not included, add them to the input dict using their default values. + Consolidate any entries of **inputs** with InputPorts as keys to Mechanism or Composition entries + If any INPUT nodes of Composition are not included, add them to the input_dict using their default values. + Note: all entries must specify either a single trial's worth of input, or the same number as all others. Returns ------- `dict` : - Input dict, with added entries for any input Nodes for which input was not provided + Input dict, with added entries for any input Nodes or Ports for which input was not provided """ - # Check that all of the Nodes listed in the inputs dict are INPUT Nodes in the Composition + # # FIX: 3/12/22 - NEED TO PREPROCESS PORTS FOR EACH NODE TO DETERMINE MAXIMUM NUMBER OF TRIALS FOR EACH, + # # TO SET mech_shape FOR BELOW, AND ASSIGN FILLERS FOR ONES THAT ARE SHORT OF THE MAX LENGTH + # # SHOULD ALSO ENFORCE 1 OR SAME NUMBER FOR ALL PORTS, AND FILL IN FOR ONES THAT ARE SHORT OF MAX + # # (NO NEED TO DO SO FOR MECH OR COMP SPECS SINCE THOSE ARE TESTED IN _validate_input_shapes + # # ALSO NO NEED TO WORRY ABOUT DIFFERENCES ACROSS NODES, AS THAT TOO WILL BE TESTED THERE + + # Validate that keys for inputs are all legal entries + bad_entries = [repr(key) for key in inputs + if not isinstance(key, (InputPort, Mechanism, Composition))] + if bad_entries: + assert False, f"One or more entries are specified in the inputs for '{self.name}' that are not " \ + f"a Mechanism, Composition, or an InputPort of one: {', '.join(bad_entries)}." + + input_dict = {} input_nodes = self.get_nodes_by_role(NodeRole.INPUT) + remaining_inputs = set(inputs) + + # Construct input_dict from input_nodes of self + for INPUT_Node in input_nodes: + + # If entry is for an INPUT_Node of self, assign the entry directly to input_dict and proceed to next + if INPUT_Node in inputs: + input_dict[INPUT_Node] = inputs[INPUT_Node] + remaining_inputs.remove(INPUT_Node) + continue - for node in inputs.keys(): - if node not in input_nodes: - if not isinstance(node, (Mechanism, Composition)): - raise CompositionError(f'{node} in "inputs" dict for {self.name} is not a ' - f'{Mechanism.__name__} or {Composition.__name__}.') + # If INPUT_Node is a Composition, get its input_CIM as mech + mech = INPUT_Node if isinstance(INPUT_Node, Mechanism) else INPUT_Node.input_CIM + INPUT_input_ports = mech.input_ports + input_port_entries = {} + inputs_to_remove = set() + + # Look for entries of inputs dict with keys that are input_ports of mech, or Composition's input_CIM + # Note: need to cycle through all inputs before constructing entries for INPUT Node + # since there may be multiple Port entries for a given INPUT Node that may differ in their specs, + # so need to process all of them to determine proper shape for input + for input_recvr in remaining_inputs: + + # Get input spec and standardize port spec as 2d to deal with any specs in time series format + port_inputs = np.atleast_2d(inputs[input_recvr]) + + # Entry is InputPort of INPUT_Node itself (usually of a standard Mechanism, but could be of input_CIM) + if input_recvr in mech.input_ports: + input_port_entries[input_recvr] = port_inputs + inputs_to_remove.add(input_recvr) + + # If INPUT_Node is a Composition, check if input_recvr is for an INPUT Node nested under it; + # if so, get the input_port(s) of mech.input_CIM to which its inputs correspond + elif (isinstance(INPUT_Node, Composition) + and ((input_recvr.owner if isinstance(input_recvr, Port) else input_recvr) in + INPUT_Node._get_nested_nodes_with_same_roles_at_all_levels(INPUT_Node, NodeRole.INPUT))): + if isinstance(input_recvr, Port): + # Spec is for Port so assign to entry for corresponding input_CIM_input_port of INPUT_Node + input_ports = [input_recvr] + else: + # Spec is for Mechanism or Composition so get its input_ports + input_ports = (input_recvr.input_ports if isinstance(input_recvr, Mechanism) + else input_recvr.input_CIM.input_ports) + # For each port in input_ports of Mech or Compositions input_CIM + for i, input_port in enumerate(input_ports): + # Get corresponding InputPort of input_CIM for INPUT_Node + input_CIM_input_port, _ = self._get_external_cim_input_port(input_port, self) + assert input_CIM_input_port.owner == mech, \ + f"PROGRAM ERROR: Unexpected input_CIM_input_port retrieved for entry ({input_recvr}) " \ + f"in inputs to '{self.name}'." + # Assign spec for InputPort to entry for corresponding InputPort on input_CIM of INPUT_Node + input_port_entries[input_CIM_input_port] = port_inputs[i] + inputs_to_remove.add(input_recvr) + + # Get max number of trials across specified input_ports of INPUT_Node + max_num_trials = 1 + for port in input_port_entries: + assert mech == port.owner + port_input = input_port_entries[port] + # Get number of trials of input specified for Port + num_trials = len(port_input) + if max_num_trials != 1 and num_trials not in {1, max_num_trials}: + raise CompositionError(f"Number of trials of input specified for {port.full_name} of {node.name} " + f"({num_trials}) is different from the number ({max_num_trials}) " + f"specified for one or more others.") + max_num_trials = max(num_trials, max_num_trials) + + # Construct node_input_shape based on max_num_trials across all input_ports for mech + # - shape as 3d by adding outer dim = max_num trials to accommodate potential trial-series input + node_input = np.empty(tuple([max_num_trials] + + list(np.array(mech.external_input_shape).shape)), + dtype='object').tolist() + # - move ports to outer access for processing below + node_input = np.swapaxes(np.atleast_3d(np.array(node_input, dtype=object)),0,1).tolist() + + # Assign specs to ports of INPUT_Node, using ones in input_port_entries or defaults + for i, port in enumerate(INPUT_input_ports): + if port in input_port_entries: + # Assume input is for all trials + port_spec = np.atleast_2d(input_port_entries[port]).tolist() + if len(port_spec) < max_num_trials: + # If input is not for all trials, ensure that it is only for a single trial + assert len(port_spec) == 1, f"PROGRAM ERROR: Length of port_spec for '{port.full_name}' " \ + f"in input to '{self.name}' ({len(port_spec)}) should now be " \ + f"1 or {max_num_trials}." + # Assign the input for the single trial over all trials + port_spec = [np.array(port_spec[0]).tolist()] * max_num_trials else: - raise CompositionError(f"{node.name} in inputs dict for {self.name} is not one of its INPUT nodes.") + # Assign default input to Port for all trials + port_spec = [np.array(port.default_input_shape).tolist()] * max_num_trials + node_input[i] = port_spec + + # Put trials back in outer axis + input_dict[INPUT_Node] = np.swapaxes(np.atleast_3d(np.array(node_input, dtype=object)),0,1).tolist() + remaining_inputs = remaining_inputs - inputs_to_remove - # If any INPUT Nodes of the Composition are not specified, add and assign default_external_input_values + if remaining_inputs: + raise CompositionError(f"The following items specified in the 'inputs' arg of the run() method for " + f"'{self.name}' are not INPUT Nodes of that Composition (nor InputPorts " + f"of them): {remaining_inputs}.") + + # If any INPUT Nodes of the Composition are not specified, add them and assign default_external_input_values for node in input_nodes: - if node not in inputs: - inputs[node] = node.external_input_shape - return inputs + if node not in input_dict: + input_dict[node] = node.external_input_shape + + return input_dict def _parse_run_inputs(self, inputs, context=None): """ @@ -8860,6 +9121,9 @@ def _validate_execution_inputs(self, inputs): for node, inp in inputs.items(): if isinstance(node, Composition) and type(inp) == dict: inp = node._parse_input_dict(inp) + if np.array(inp).ndim == 3: + # If inp formatted for trial series, get only one one trial's worth of inputs to test + inp = np.squeeze(inp, 0) inp = self._validate_single_input(node, inp) if inp is None: raise CompositionError(f"Input stimulus ({inp}) for {node.name} is incompatible " @@ -8918,8 +9182,8 @@ def run( the inputs to each `INPUT` `Node ` of the Composition in each `TRIAL ` executed during the run (see `Composition_Execution_Inputs` for additional information about format, and `get_input_format ` method for generating an example of the input format for - the Composition). If **inputs** is not specified, the `default_variable ` for each - `INPUT` Node is used as its input on `TRIAL `. + the Composition). If **inputs** is not specified, the `default_variable ` for + each `INPUT` Node is used as its input on `TRIAL `. num_trials : int : default 1 typically, the composition will infer the number of trials from the length of its input specification. @@ -9710,8 +9974,8 @@ def execute( a dictionary containing a key-value pair for each `Node ` in the Composition that receives inputs from the user. For each pair, the key is the `Node ` (a `Mechanism ` or `Composition`) and the value is an input, the shape of which must match the Node's - default variable. If **inputs** is not specified, the `default_variable ` for - each `INPUT` Node is used as its input (see `Input Formats ` for + default variable. If **inputs** is not specified, the `default_variable ` + for each `INPUT` Node is used as its input (see `Input Formats ` for additional details). clamp_input : SOFT_CLAMP : default SOFT_CLAMP @@ -10494,8 +10758,8 @@ def __call__(self, *args, **kwargs): bad_args_str = ", ".join([str(arg) for arg in args] + list(kwargs.keys())) raise CompositionError(f"Composition ({self.name}) called with illegal argument(s): {bad_args_str}") - # Alias of get_input_format(easy mistake to make) def get_inputs_format(self, **kwargs): + """Alias of get_input_format (easy mistake to make)""" return self.get_input_format(**kwargs, alias="get_inputs_format") def get_input_format(self, @@ -10660,7 +10924,8 @@ def _get_inputs(comp, nesting_level=1, use_labels=False, template_dict=str): else: formatted_input = _get_inputs(self, 1, use_labels) if show_nested_input_nodes: - preface = f"\nInputs to (nested) INPUT Nodes of {self.name} for {num_trials} trials:" + plural = 's' if num_trials > 1 else '' + preface = f"\nInputs to (nested) INPUT Nodes of {self.name} for {num_trials} trial{plural}:" epilog = f"\n\nFormat as follows for inputs to run():\n" \ f"{self.get_input_format(form=form, num_trials=num_trials)}" return preface + formatted_input[:-1] + epilog @@ -10729,7 +10994,7 @@ def get_results_by_nodes(self, # Get all values for all OUTPUT Nodes if use_labels: # Get labels for corresponding values - values = [node.output_labels for node in output_nodes] + values = [node.labeled_output_values for node in output_nodes] else: values = self.results[-1] or self.output_values diff --git a/psyneulink/core/compositions/parameterestimationcomposition.py b/psyneulink/core/compositions/parameterestimationcomposition.py index 98b2df8b631..0ab934d0fc2 100644 --- a/psyneulink/core/compositions/parameterestimationcomposition.py +++ b/psyneulink/core/compositions/parameterestimationcomposition.py @@ -52,7 +52,7 @@ attribute that it estimates best satisfy either of those conditions, and the results of running the `model ` with those parameters in its `results ` attribute. The arguments below are the primary ones used to configure a ParameterEstimationComposition for either -`ParameterEstimationComposition_Data_Fitting` or `ParameterEstimationComposition_Optimization`), followed by +`ParameterEstimationComposition_Data_Fitting` or `ParameterEstimationComposition_Optimization`, followed by sections that describe arguments specific to each. .. _ParameterEstimationComposition_Model: @@ -73,7 +73,7 @@ * **outcome_variables** - specifies the `OUTPUT` `Nodes ` of the `model `, the `values ` of which are used - to evaluate the fit of the different combination of parameter values sampled. + to evaluate the fit of the different combinations of parameter values sampled. * **num_estimates** - specifies the number of independent samples that are estimated for a given combination of parameter values. @@ -91,15 +91,15 @@ .. _ParameterEstimationComposition_Data: * **data** - specifies the data to which the `outcome_variables ` - are fit in the estimation process. They must be in a format that aligns the specification of - `outcome_variables `. + are fit in the estimation process. They must be in a format that aligns with the specification of + the `outcome_variables `. COMMENT: FIX: GET MORE FROM DAVE HERE COMMENT * **optimization_function** - specifies the function used to compare the `values ` of the `outcome_variables ` with the **data**, and search over values - of the `parameters ` that maximize the fit. This must be either + of the `parameters ` that maximize the fit. This must be either a `ParameterEstimationFunction` or a subclass of that. By default, ParameterEstimationFunction uses maximum likelihood estimation (MLE) to compare the `outcome_variables ` and the data, and diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index 855a70fe47a..33552e6d207 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -74,7 +74,7 @@ 'MATRIX', 'MATRIX_KEYWORD_NAMES', 'MATRIX_KEYWORD_SET', 'MATRIX_KEYWORD_VALUES', 'MATRIX_KEYWORDS','MatrixKeywords', 'MAX_ABS_DIFF', 'MAX_ABS_INDICATOR', 'MAX_ONE_HOT', 'MAX_ABS_ONE_HOT', 'MAX_ABS_VAL', 'MAX_EXECUTIONS_BEFORE_FINISHED', 'MAX_INDICATOR', 'MAX_VAL', 'MAYBE', 'MEAN', - 'MECHANISM', 'MECHANISM_COMPONENT_CATEGORY', 'MECHANISM_DEFAULT', 'MECHANISM_DEFAULTInputValue', + 'MECHANISM', 'MECHANISM_COMPONENT_CATEGORY', 'MECHANISM_DEFAULT', 'MECHANISM_DEFAULT_INPUT_VALUE', 'MECHANISM_DEFAULTParams', 'MECHANISM_EXECUTED_LOG_ENTRY', 'MECHANISM_NAME', 'MECHANISM_PARAM_VALUE', 'MECHANISM_TYPE', 'MECHANISM_VALUE', 'MEDIAN', 'METRIC', 'MIN_VAL', 'MIN_ABS_VAL', 'MIN_ABS_INDICATOR', 'MOD_AFFERENTS', 'MODE', 'MODULATES','MODULATION', 'MODULATORY_PROJECTION', 'MODULATORY_SIGNAL', @@ -683,7 +683,7 @@ def _is_metric(metric): DEFAULT_PROCESSING_MECHANISM = "DefaultProcessingMechanism" PROCESS_DEFAULT_MECHANISM = "ProcessDefaultMechanism" MECHANISM_TYPE = "Mechanism Type" # Used in mechanism dict specification (e.g., in process.pathway[]) -MECHANISM_DEFAULTInputValue = "Mechanism Default Input Value " # Used in mechanism specification dict +MECHANISM_DEFAULT_INPUT_VALUE = "Mechanism Default Input Value " # Used in mechanism specification dict MECHANISM_PARAM_VALUE = "Mechanism Parameter Value" # Used to specify mechanism param value MECHANISM_DEFAULTParams = "Mechanism Default Parameters" # Used in mechanism specification dict CONDITION = 'condition' diff --git a/requirements.txt b/requirements.txt index cf041b9b9ca..f2659519dcc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,5 +16,5 @@ torch>=1.8.0, <2.0.0; (platform_machine == 'AMD64' or platform_machine == 'x86_6 typecheck-decorator<=1.2 leabra-psyneulink<=0.3.2 rich>=10.1, <10.13 -pandas<=1.0.5 +pandas<=1.4.1 fastkde==1.0.19 \ No newline at end of file diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 695f941f5a1..1f0153dc6e7 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -1265,6 +1265,7 @@ def test_composition_learning_pathway_dict_with_no_learning_fct_in_tuple_error(s class TestProperties: + @pytest.mark.control @pytest.mark.parametrize("control_spec", [CONTROL, PROJECTIONS]) def test_properties(self, control_spec): @@ -1389,6 +1390,7 @@ def test_two_origins_pointing_to_recursive_pair(self): assert B in comp.get_nodes_by_role(NodeRole.CYCLE) assert C in comp.get_nodes_by_role(NodeRole.RECURRENT_INIT) + @pytest.mark.control def test_controller_objective_mech_not_terminal(self): comp = Composition() A = ProcessingMechanism(name='A') @@ -1419,6 +1421,7 @@ def test_controller_objective_mech_not_terminal(self): # assert comp.controller.objective_mechanism in comp.get_nodes_by_role(NodeRole.OUTPUT) assert comp.controller.objective_mechanism not in comp.get_nodes_by_role(NodeRole.OUTPUT) + @pytest.mark.control def test_controller_objective_mech_not_terminal_fall_back(self): comp = Composition() A = ProcessingMechanism(name='A') @@ -4230,6 +4233,7 @@ def test_nested_run_differing_num_trials(self, comp_mode): outer.run(inputs=input, execution_mode=comp_mode) + @pytest.mark.control def test_invalid_projection_deletion_when_nesting_comps(self): oa = pnl.TransferMechanism(name='oa') ob = pnl.TransferMechanism(name='ob') @@ -4726,6 +4730,7 @@ def test_four_level_nested_transfer_mechanism_composition_parallel(self): ret = comp_lvl0.run(inputs={comp_lvl1: {comp_lvl2: {comp_lvl3a: [[1.0]], comp_lvl3b: [[1.0]]}}}) assert np.allclose(ret, [[[0.52497918747894]], [[0.52497918747894]]]) + @pytest.mark.control def test_four_level_nested_OCM_control(self): p_lvl3 = ProcessingMechanism(name='p_lvl3') @@ -4747,6 +4752,7 @@ def test_four_level_nested_OCM_control(self): result = c_lvl0.run([5]) assert result == [150] + @pytest.mark.control def test_four_level_nested_dual_OCM_control(self): p_lvl3 = ProcessingMechanism(name='p_lvl3') @@ -4781,6 +4787,7 @@ def test_four_level_nested_dual_OCM_control(self): result = c_lvl0.run([5]) assert result == [4500] + @pytest.mark.control @pytest.mark.parametrize('nesting', ("unnested", "nested")) def test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(self, nesting): pnl.clear_registry() @@ -5650,11 +5657,19 @@ def test_function(trial_num): xor_comp.learn(inputs=test_function, num_trials=4) + @pytest.mark.control @pytest.mark.parametrize( - "with_outer_controller,with_inner_controller", - [(True, True), (True, False), (False, True), (False, False)] + "controllers, results",[ + ('none', [[-2], [1]]), + ('inner', [[-2], [10]]), + ('outer', [[-2], [10]]), + ('inner_and_outer', [[-2], [100]]), + ] + ) + @pytest.mark.parametrize( + "inputs_arg",['inputs_dict','generator_function','generator_instance'] ) - def test_input_type_equivalence(self, with_outer_controller, with_inner_controller): + def test_input_type_equivalence(self, controllers, results, inputs_arg): # instantiate mechanisms and inner comp ia = pnl.TransferMechanism(name='ia') ib = pnl.TransferMechanism(name='ib') @@ -5666,7 +5681,7 @@ def test_input_type_equivalence(self, with_outer_controller, with_inner_controll icomp.add_projection(pnl.MappingProjection(), sender=ia, receiver=ib) # add controller to inner comp - if with_inner_controller: + if controllers in {'inner', 'inner_and_outer'}: icomp.add_controller( pnl.OptimizationControlMechanism( agent_rep=icomp, @@ -5675,7 +5690,7 @@ def test_input_type_equivalence(self, with_outer_controller, with_inner_controll objective_mechanism=pnl.ObjectiveMechanism( monitor=ib.output_port, function=pnl.SimpleIntegrator, - name="oController Objective Mechanism" + name="iController Objective Mechanism" ), function=pnl.GridSearch(direction=pnl.MAXIMIZE), control_signals=[pnl.ControlSignal(projections=[(pnl.SLOPE, ia)], @@ -5693,7 +5708,7 @@ def test_input_type_equivalence(self, with_outer_controller, with_inner_controll ocomp.add_node(icomp) # add controller to outer comp - if with_outer_controller: + if controllers in {'outer', 'inner_and_outer'}: ocomp.add_controller( pnl.OptimizationControlMechanism( agent_rep=ocomp, @@ -5715,9 +5730,9 @@ def test_input_type_equivalence(self, with_outer_controller, with_inner_controll ) # set up input using three different formats: - # 1) generator function - # 2) instance of generator function - # 3) inputs dict + # 1) inputs dict + # 2) generator function + # 3) instance of generator function inputs_dict = { icomp: { @@ -5736,20 +5751,27 @@ def inputs_generator_function(): inputs_generator_instance = inputs_generator_function() - # run Composition with all three input types and assert that results are as expected. - ocomp.run(inputs=inputs_generator_function) - ocomp.run(inputs=inputs_generator_instance) - ocomp.run(inputs=inputs_dict) - - # assert results are as expected - if not with_inner_controller and not with_outer_controller: - assert ocomp.results[0:2] == ocomp.results[2:4] == ocomp.results[4:6] == [[-2], [1]] - elif with_inner_controller and not with_outer_controller or \ - with_outer_controller and not with_inner_controller: - assert ocomp.results[0:2] == ocomp.results[2:4] == ocomp.results[4:6] == [[-2], [10]] - else: - assert ocomp.results[0:2] == ocomp.results[2:4] == ocomp.results[4:6] == [[-2], [100]] + inputs_source = { + 'inputs_dict': inputs_dict, + 'generator_function': inputs_generator_function, + 'generator_instance': inputs_generator_instance + }[inputs_arg] + + # # Version with reporting for debugging purposes: + # ib.reportOutputPref=[pnl.VALUE, pnl.VARIABLE] + # icomp.controller.reportOutputPref = pnl.ReportOutput.ON + # ocomp.controller.reportOutputPref = pnl.ReportOutput.FULL + # ocomp.run(inputs=inputs_dict, + # report_output=pnl.ReportOutput.FULL, + # report_progress=pnl.ReportProgress.ON, + # report_simulations=pnl.ReportSimulations.ON, + # report_to_devices=pnl.ReportDevices.DIVERT + # ) + # actual_output = ocomp.rich_diverted_reports + # run Composition with all three input types and assert that results are as expected. + ocomp.run(inputs=inputs_source) + assert ocomp.results == results expected_format_strings = \ [ @@ -7017,6 +7039,7 @@ def test_LEARNING_bp(self): # Validate that TERMINAL is LearningMechanism that Projects to first MappingProjection in learning_pathway (comp.get_nodes_by_role(NodeRole.TERMINAL))[0].efferents[0].receiver.owner.sender.owner == A + @pytest.mark.control def test_controller_role(self): comp = Composition() A = ProcessingMechanism(name='A') diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 61f2acad11f..27f90219c29 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -297,10 +297,6 @@ def test_partial_deferred_init(self, state_features_option): ocomp.add_controller( pnl.OptimizationControlMechanism( agent_rep=ocomp, - # state_features=[initial_node_a.input_port, - # deferred_node.input_port], - # state_features={initial_node_a:initial_node_a.input_port, - # deferred_node:deferred_node.input_port}, state_features = state_features, name="Controller", objective_mechanism=pnl.ObjectiveMechanism( @@ -314,7 +310,7 @@ def test_partial_deferred_init(self, state_features_option): deferred_node_control_signal ]) ) - assert ocomp.controller.state_features == {initial_node_a: initial_node_a.input_port, + assert ocomp.controller.state_features == {initial_node_a.input_port: initial_node_a.input_port, 'EXPECTED INPUT NODE 1 OF ocomp':deferred_node.input_port} if state_features_option in {'list', 'shadow_inputs_dict'}: @@ -327,7 +323,7 @@ def test_partial_deferred_init(self, state_features_option): else: expected_text = 'The \'state_features\' specified for \'Controller\' contains an item (deferred) ' \ - 'that is not an INPUT Node of its agent_rep (\'ocomp\'); only INPUT Nodes can be ' \ + 'that is not an INPUT Node within its agent_rep (\'ocomp\'); only INPUT Nodes can be ' \ 'in a set or used as keys in a dict used to specify \'state_features\'.' with pytest.raises(pnl.OptimizationControlMechanismError) as error_text: @@ -335,8 +331,8 @@ def test_partial_deferred_init(self, state_features_option): assert expected_text in error_text.value.error_value ocomp.add_linear_processing_pathway([deferred_node, initial_node_b]) - assert ocomp.controller.state_features == {initial_node_a: initial_node_a.input_port, - deferred_node: deferred_node.input_port} + assert ocomp.controller.state_features == {initial_node_a.input_port: initial_node_a.input_port, + deferred_node.input_port: deferred_node.input_port} result = ocomp.run({ initial_node_a: [1], @@ -782,10 +778,10 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c messages = [ # 0 - f"There are fewer '{pnl.STATE_FEATURES}' specified for 'OptimizationControlMechanism-0' than the number of " - f"INPUT Nodes of its agent_rep ('OUTER COMP'); the remaining inputs will be assigned default values " - f"when 'OUTER COMP`s 'evaluate' method is executed. If this is not the desired configuration, use its " - f"get_inputs_format() method to see the format for all of its inputs.", + f"There are fewer '{pnl.STATE_FEATURES}' specified for 'OptimizationControlMechanism-0' than the number " + f"of InputPort's for all of the INPUT Nodes of its agent_rep ('OUTER COMP'); the remaining inputs will be " + f"assigned default values when 'OUTER COMP`s 'evaluate' method is executed. If this is not the desired " + f"behavior, use its get_inputs_format() method to see the format for its inputs.", # 1 f'\'Attempt to shadow the input to a node (IB) in a nested Composition of OUTER COMP ' @@ -800,37 +796,38 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c f'that are missing from \'OUTER COMP\' and any Compositions nested within it."', # 4 - "The 'state_features' argument has been specified for 'OptimizationControlMechanism-0' that is using a " - "Composition ('OUTER COMP') as its agent_rep, but they are not compatible with the inputs required by " - "its 'agent_rep': 'Input stimulus ([0.0]) for OB is incompatible with the shape of its external input " - "([0.0 0.0 0.0]).' Use the get_inputs_format() method of 'OUTER COMP' to see the required format, or " - "remove the specification of 'state_features' from the constructor for OptimizationControlMechanism-0 " - "to have them automatically assigned.", + f"The '{pnl.STATE_FEATURES}' argument has been specified for 'OptimizationControlMechanism-0' that is using " + f"a Composition ('OUTER COMP') as its agent_rep, but some of the specifications are not compatible with the " + f"inputs required by its 'agent_rep': 'Input stimulus ([0.0]) for OB is incompatible with the shape of its " + f"external input ([0.0 0.0 0.0]).' Use the get_inputs_format() method of 'OUTER COMP' to see the required " + f"format, or remove the specification of 'state_features' from the constructor for " + f"OptimizationControlMechanism-0 to have them automatically assigned.", # 5 - f"The number of '{pnl.STATE_FEATURES}' specified for OptimizationControlMechanism-0 (4) is more than " - f"the number of INPUT Nodes (3) of the Composition assigned as its agent_rep ('OUTER COMP'). " - f"Executing OptimizationControlMechanism-0 before the additional Nodes are added as INPUT Nodes " - f"will generate an error.", + f"The '{pnl.STATE_FEATURES}' specified for OptimizationControlMechanism-0 is associated with a number of " + f"InputPorts (4) that is greater than for the InputPorts of the INPUT Nodes (3) for the Composition assigned " + f"as its agent_rep ('OUTER COMP'). Executing OptimizationControlMechanism-0 before the additional item(s) are " + f"added as (part of) INPUT Nodes will generate an error.", # 6 - f"The number of 'state_features' specified for OptimizationControlMechanism-0 (4) is more than the number " - f"of INPUT Nodes (3) of the Composition assigned as its agent_rep ('OUTER COMP'), which includes the " - f"following that are not in 'OUTER COMP': 'EXT'. Executing OptimizationControlMechanism-0 before the " - f"additional Node(s) are added as INPUT Nodes will generate an error.", + f"The '{pnl.STATE_FEATURES}' specified for OptimizationControlMechanism-0 is associated with a number of " + f"InputPorts (4) that is greater than for the InputPorts of the INPUT Nodes (3) for the Composition assigned " + f"as its agent_rep ('OUTER COMP'), which includes the following that are not (yet) in 'OUTER COMP': 'EXT'. " + f"Executing OptimizationControlMechanism-0 before the additional item(s) are added as (part of) INPUT Nodes " + f"will generate an error.", # 7 f'"The number of \'state_features\' specified for OptimizationControlMechanism-0 (4) is more than the number ' f'of INPUT Nodes (3) of the Composition assigned as its agent_rep (\'OUTER COMP\')."', # 8 - f'The \'state_features\' specified for \'OptimizationControlMechanism-0\' contains items (IA, OC) ' - f'that are not INPUT Nodes of its agent_rep (\'OUTER COMP\'); only INPUT Nodes can be in a set or ' + f'The \'state_features\' specified for \'OptimizationControlMechanism-0\' contains an item (OC) ' + f'that is not an INPUT Node within its agent_rep (\'OUTER COMP\'); only INPUT Nodes can be in a set or ' f'used as keys in a dict used to specify \'state_features\'.', # 9 f'The \'state_features\' specified for \'OptimizationControlMechanism-0\' contains an item (IA) ' - f'that is not an INPUT Node of its agent_rep (\'OUTER COMP\'); only INPUT Nodes can be in a set ' + f'that is not an INPUT Node within its agent_rep (\'OUTER COMP\'); only INPUT Nodes can be in a set ' f'or used as keys in a dict used to specify \'state_features\'.', # 10 @@ -842,6 +839,11 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c f"The '{pnl.STATE_FEATURES}' argument for 'OptimizationControlMechanism-0' includes one or more Compositions " f"('INNER COMP') in the SHADOW_INPUTS dict specified for its '{pnl.STATE_FEATURES}' argument; these must be " f"replaced by direct references to the Mechanisms (or their InputPorts) within them to be shadowed.", + + # 12 + f"The '{pnl.STATE_FEATURES}' argument for 'OptimizationControlMechanism-0' has one or more items in the " + f"list specified for 'SHADOW_INPUTS' ('IA') that are not (part of) any INPUT Nodes of its 'agent_rep' " + f"('OUTER COMP')." ] state_feature_args = [ @@ -849,18 +851,32 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ('full_list_spec', None, None, None), ('list_spec_with_none', None, None, None), ('input_dict_spec', None, None, None), + ('input_dict_spec_short', None, None, None), + ('set_spec_short', None, None, None), + ('set_spec', None, None, None), + ('set_spec_port', None, None, None), + ('automatic_assignment', None, None, None), + ('shadow_inputs_dict_spec', None, None, None), + ('shadow_inputs_dict_spec_w_none', None, None, None), + ('misplaced_shadow', messages[1], None, pnl.CompositionError), + ('ext_shadow', messages[2], None, pnl.OptimizationControlMechanismError), + ('ext_output_port', messages[3], None, pnl.OptimizationControlMechanismError), ('input_format_wrong_shape', messages[4], None, pnl.OptimizationControlMechanismError), ('too_many_inputs_warning', messages[5], None, UserWarning), ('too_many_w_node_not_in_composition_warning', messages[6], None, UserWarning), ('too_many_inputs_error', messages[7], None, pnl.OptimizationControlMechanismError), ('bad_dict_spec_warning', messages[8], None, UserWarning), ('bad_dict_spec_error', messages[8], None, pnl.OptimizationControlMechanismError), - ('bad_set_spec_warning', messages[0], messages[9], UserWarning), - ('bad_set_spec_error', messages[9], None, pnl.OptimizationControlMechanismError), + ('bad_shadow_inputs_dict_spec_error', messages[12], None, pnl.OptimizationControlMechanismError), ('comp_in_list_spec', messages[10], None, pnl.OptimizationControlMechanismError), ('comp_in_shadow_inupts_spec', messages[11], None, pnl.OptimizationControlMechanismError) ] + if len(state_feature_args) != 23: + print("\n\n**********************************************************************************************") + print("*** RESTORE state_feature_args IN test_ocm_state_feature_specs_and_warnings_and_errors() *****") + print("***********************************************************************************************") + @pytest.mark.control @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x[0] for x in state_feature_args]) @pytest.mark.parametrize('obj_mech', [ @@ -869,6 +885,7 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c None ]) def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_args, obj_mech): + """See test_nested_composition_as_agent_rep() for additional tests of state_features specification.""" test_condition = state_feature_args[0] message_1 = state_feature_args[1] @@ -890,13 +907,14 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg state_features_dict = { # Legal state_features specifications - 'partial_legal_list_spec': [oa.output_port], + 'partial_legal_list_spec': [oa.output_port], # Note: only specifies ia; oa and ob assigned default inputs 'full_list_spec': [ia.input_port, oa.output_port, [3,1,2]], 'list_spec_with_none': [ia.input_port, None, [3,1,2]], - 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK - 'input_dict_spec_short': {ob:ob.output_port, oa:oc.input_port}, # Note: missing oa spec and out of order - 'set_spec': {ob, icomp, oa}, # Note: out of order is OK, and use of Nested comp as spec + 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: use icomp & out of order is OK + 'input_dict_spec_short': {ob:ob.output_port, oa:oc.input_port}, # Note: missing ia spec and out of order 'set_spec_short': {oa}, + 'set_spec': {ob, icomp, oa}, # Note: out of order is OK, and use of Nested comp as spec + 'set_spec_port': {ob.input_port, icomp, oa}, 'automatic_assignment': None, 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, # <- ia & ob OK BECAUSE JUST FOR SHADOWING 'shadow_inputs_dict_spec_w_none': {pnl.SHADOW_INPUTS:[ia, None, ob]}, @@ -911,8 +929,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'too_many_w_node_not_in_composition_warning': [ia, oa, ob, ext], 'bad_dict_spec_warning': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node 'bad_dict_spec_error': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # ia & oc are not *ocomp* INPUT Nodes - 'bad_set_spec_warning': {ob, ia}, # elicits short spec warning - 'bad_set_spec_error': {ob, ia}, # elicits INPUT Node warning (for ia) + 'bad_shadow_inputs_dict_spec_error': {pnl.SHADOW_INPUTS:[ia.output_port, None, ob]}, # not INPUT InputPort 'comp_in_list_spec':[icomp, oa.output_port, [3,1,2]], # FIX: REMOVE ONCE TUPLE FORMAT SUPPORTED 'comp_in_shadow_inupts_spec':{pnl.SHADOW_INPUTS:[icomp, oa, ob]} } @@ -936,20 +953,21 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'OA[OutputPort-0]', - 'OB DEFAULT_VARIABLE'] - assert ocm.state_features == {icomp: ia.input_port, oa: oa.output_port, ob: [3, 1, 2]} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [3, 1, 2]])) - + 'OB[InputPort-0] DEFAULT_VARIABLE'] + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: oa.output_port, + ob.input_port: [3, 1, 2]} if test_condition == 'list_spec_with_none': assert len(ocm.state_input_ports) == 2 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'OB DEFAULT_VARIABLE'] - assert ocm.state_features == {icomp: ia.input_port, oa: None, ob: [3, 1, 2]} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [3, 1, 2]])) - + 'OB[InputPort-0] DEFAULT_VARIABLE'] + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: None, + ob.input_port: [3, 1, 2]} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [3, 1, 2]])) elif test_condition == 'input_dict_spec': assert len(ocm.state_input_ports) == 3 @@ -957,72 +975,90 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'Shadowed input of OC[InputPort-0]', 'OB[OutputPort-0]'] # 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK - assert ocm.state_features == {icomp:ia.input_port, oa:oc.input_port, ob:ob.output_port} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: oc.input_port, + ob.input_port: ob.output_port} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) elif test_condition == 'input_dict_spec_short': assert len(ocm.state_input_ports) == 2 assert ocm.state_input_ports.names == ['Shadowed input of OC[InputPort-0]', 'OB[OutputPort-0]'] - assert ocm.state_features == {icomp: None, oa:oc.input_port, ob:ob.output_port} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) - - elif test_condition == 'set_spec': - assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OA[InputPort-0]', - 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {icomp:ia.input_port, oa:oa.input_port, ob:ob.input_port} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) + assert ocm.state_features == {ia.input_port: None, + oa.input_port: oc.input_port, + ob.input_port: ob.output_port} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) elif test_condition == 'set_spec_short': assert len(ocm.state_input_ports) == 1 assert ocm.state_input_ports.names == ['Shadowed input of OA[InputPort-0]'] # 'set_spec': {ob, icomp, oa}, # Note: out of order is OK - assert ocm.state_features == {icomp:None, oa:oa.input_port, ob:None} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) + assert ocm.state_features == {ia.input_port: None, + oa.input_port: oa.input_port, + ob.input_port: None} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) + + elif test_condition in {'set_spec', 'set_spec_port'}: + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OA[InputPort-0]', + 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: oa.input_port, + ob.input_port: ob.input_port} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) elif test_condition == 'automatic_assignment': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {ia: ia.input_port, oa: oa.input_port, ob: ob.input_port} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: oa.input_port, + ob.input_port: ob.input_port} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) elif test_condition == 'shadow_inputs_dict_spec': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {icomp: ia.input_port, oa: oa.input_port, ob: ob.input_port} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: oa.input_port, + ob.input_port: ob.input_port} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) elif test_condition == 'shadow_inputs_dict_spec_w_none': assert len(ocm.state_input_ports) == 2 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {icomp: ia.input_port, oa: None, ob: ob.input_port} - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, [[0.], [0.], [0, 0, 0]])) + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: None, + ob.input_port: ob.input_port} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) elif exception_type is UserWarning: # These also produce errors, tested below if test_condition in {'too_many_inputs_warning', 'too_many_w_node_not_in_composition_warning', - 'bad_dict_spec_warning', - 'bad_set_spec_warning'}: + 'bad_dict_spec_warning'}: with pytest.warns(UserWarning) as warning: ocomp.add_controller(ocm) - assert warning[0].message.args[0] == message_1 - if test_condition == 'bad_set_spec_warning': - assert message_2 == warning[1].message.args[0] # since set, order of ob and ia is not reliable + assert message_1 in [warning[i].message.args[0] for i in range(len(warning))] else: with pytest.warns(UserWarning) as warning: ocomp.add_controller(ocm) @@ -1031,9 +1067,13 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert len(ocm.state_input_ports) == 1 assert ocm.state_input_ports.names == ['OA[OutputPort-0]'] # Note: oa is assigned to icomp due to ordering: - assert ocm.state_features == {icomp: oa.output_port, oa: None, ob: None} - assert warning[0].message.args[0] == message_1 - assert ocm.state_features == {icomp: oa.output_port, oa: None, ob: None} + assert ocm.state_features == {ia.input_port: oa.output_port, + oa.input_port: None, + ob.input_port: None} + assert message_1 in [warning[i].message.args[0] for i in range(len(warning))] + assert ocm.state_features == {ia.input_port: oa.output_port, + oa.input_port: None, + ob.input_port: None} else: with pytest.raises(exception_type) as error: @@ -1113,8 +1153,8 @@ def test_state_feature_function_specs(self, state_fct_assignments): inputs = {A:[1,2], B:[1,2], C:[1,2]} result = comp.run(inputs=inputs, context='test') assert result == [[24.]] - assert all(np.allclose(expected, actual) - for expected, actual in zip(ocm.parameters.state_feature_values.get('test'), + assert all(np.allclose(actual, expected) + for actual, expected in zip(list(ocm.parameters.state_feature_values.get('test').values()), [[20],[[1],[2]],[3]])) else: assert isinstance(ocm.state_input_ports[0].function, pnl.LinearCombination) @@ -1124,7 +1164,7 @@ def test_state_feature_function_specs(self, state_fct_assignments): result = comp.run(inputs=inputs, context='test') assert result == [[24.]] assert all(np.allclose(expected, actual) - for expected, actual in zip(ocm.parameters.state_feature_values.get('test'), + for actual, expected in zip(list(ocm.parameters.state_feature_values.get('test').values()), [[2],[2],[2]])) @pytest.mark.control @@ -1133,7 +1173,7 @@ def test_ocm_state_and_state_dict(self): ib = pnl.ProcessingMechanism(name='IB') ic = pnl.ProcessingMechanism(name='IC') oa = pnl.ProcessingMechanism(name='OA') - ob = pnl.ProcessingMechanism(name='OB') + ob = pnl.ProcessingMechanism(name='OB', size=3) oc = pnl.ProcessingMechanism(name='OC') icomp = pnl.Composition(pathways=[ia,ib,ic], name='INNER COMP') ocomp = pnl.Composition(pathways=[icomp], name='OUTER COMP') @@ -1142,8 +1182,7 @@ def test_ocm_state_and_state_dict(self): ocm = pnl.OptimizationControlMechanism( state_features=[ia.input_port, oa.output_port, - # ob], - [3,1,2]], + [3,1,2]], # <- ob objective_mechanism=[ic,ib], function=pnl.GridSearch(), allow_probes=True, @@ -1167,9 +1206,9 @@ def test_ocm_state_and_state_dict(self): assert keys[4] == (oc.parameter_ports[pnl.INTERCEPT], oc, ocomp, 4) assert keys[5] == (oc.parameter_ports[pnl.SLOPE], oc, ocomp, 4) ocomp.run() - assert all(np.allclose(expected,actual) - for expected,actual in zip(ocm.state_feature_values, - [[0.], [0.], [3, 1, 2]])) + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [3, 1, 2]])) def test_modulation_of_control_signal_intensity_cost_function_MULTIPLICATIVE(self): # tests multiplicative modulation of default intensity_cost_function (Exponential) of @@ -1624,7 +1663,6 @@ def test_two_tier_ocm(self): pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, state_features=[taskLayer.input_port, stimulusInfo.input_port], - state_feature_function=pnl.Buffer(history=2), name="Controller", objective_mechanism=pnl.ObjectiveMechanism( monitor=[(pnl.PROBABILITY_UPPER_THRESHOLD, @@ -1650,7 +1688,6 @@ def test_two_tier_ocm(self): outerComposition.add_controller( pnl.OptimizationControlMechanism(agent_rep=stabilityFlexibility, state_features=[taskLayer.input_port, stimulusInfo.input_port], - state_feature_function=pnl.Buffer(history=2), name="OuterController", objective_mechanism=pnl.ObjectiveMechanism( monitor=[(pnl.PROBABILITY_UPPER_THRESHOLD, decisionMaker)], @@ -3003,6 +3040,11 @@ def computeAccuracy(trialInformation): inputs = {taskLayer: taskTrain, stimulusInfo: stimulusTrain} stabilityFlexibility.run(inputs) + assert np.allclose(stabilityFlexibility.results, + [[[0.0475], [0.33695222], [1.], [1.13867062e-09]], + [[0.0475], [1.13635091], [0.93038951], [0.06961049]], + [[0.0475], [0.35801411], [0.99999998], [1.77215519e-08]], + [[0.0475], [0.89706881], [0.97981972], [0.02018028]]]) @pytest.mark.parametrize('num_estimates',[None, 1, 2] ) @pytest.mark.parametrize('rand_var',[False, True] ) @@ -3196,10 +3238,14 @@ def test_input_CIM_assignment(self, comp_mode): assert np.allclose(results, [[7]]) state_features_arg = [ - # 'nested_partial', # <- Specify only one of two INPUT Nodes of nested comp - # 'nested_full', # <- Specify both of two INPUT Nodes of nested comp - 'automatic', # <- Automaticaly asign state_features - 'bad' # <- Specify Mechanism not in agent_rep + 'nested_partial_set', # <- Specify only one of two INPUT Nodes of nested comp in set format + 'nested_partial_dict', # <- Specify only one of two INPUT Nodes of nested comp in dict format + 'nested_full_set', # <- Specify both of two INPUT Nodes of nested comp in set format + 'nested_full_dict', # <- Specify both of two INPUT Nodes of nested comp in dict format + 'nested_comp_set', # <- Specify nested comp as itself in set format + 'nested_comp_dict', # <- Specify nested comp as itself in dict format with a single spec for all INPUT Nodes + 'automatic', # <- Automaticaly asign state_features + 'bad' # <- Specify Mechanism not in agent_rep ] @pytest.mark.control @pytest.mark.composition @@ -3210,7 +3256,9 @@ def test_input_CIM_assignment(self, comp_mode): ids= [f"state_feature-{x}" for x in state_features_arg] ) def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_arg): - """Also tests state_features for comp nested within nested_agent_rep""" + """Also tests state_features for comp nested within nested_agent_rep. + See test_ocm_state_feature_specs_and_warnings_and_errors() for additional tests of state_features specification. + """ I1 = pnl.ProcessingMechanism(name='I1') I2 = pnl.ProcessingMechanism(name='I2') @@ -3233,10 +3281,18 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ error_text = '"\'OCM\' has \'state_features\' specified ([\'D[OutputPort-0]\']) that are ' \ 'missing from \'OUTER COMP\' and any Compositions nested within it."' # state_features = D.output_port if state_features_arg is 'bad' else None - if state_features_arg == 'nested_partial': - state_features = [A] - elif state_features_arg == 'nested_full': - state_features = [A, I1, I2] + if state_features_arg == 'nested_partial_set': + state_features = {A, I2} + elif state_features_arg == 'nested_full_set': + state_features = {A, I1, I2} + elif state_features_arg == 'nested_partial_dict': + state_features = {A:A.input_port, I2:I1.input_port} + elif state_features_arg == 'nested_full_dict': + state_features = {A:A.input_port, I1:I2.input_port, I2:I1.input_port} + elif state_features_arg == 'nested_comp_set': + state_features = {mcomp} + elif state_features_arg == 'nested_comp_dict': + state_features = {mcomp:I1} elif state_features_arg == 'automatic': state_features = None elif state_features_arg == 'bad': @@ -3260,7 +3316,26 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ else: ocomp.add_controller(ocm) ocomp.run() - assert ocm.state_features == {A:A.input_port, I1:I1.input_port, I2:I2.input_port} + if state_features_arg == 'nested_partial_set': + assert ocm.state_features == {A.input_port: A.input_port, + I1.input_port: None, + I2.input_port: I2.input_port} + elif state_features_arg == 'nested_partial_dict': + assert ocm.state_features == {A.input_port: A.input_port, + I1.input_port: None, + I2.input_port: I1.input_port} + elif state_features_arg == 'nested_full_dict': + assert ocm.state_features == {A.input_port: A.input_port, + I1.input_port: I2.input_port, + I2.input_port: I1.input_port} + elif state_features_arg == 'nested_comp_dict': + assert ocm.state_features == {A.input_port:I1.input_port, + I1.input_port: I1.input_port, + I2.input_port: I1.input_port} + else: + assert ocm.state_features == {A.input_port: A.input_port, + I1.input_port: I1.input_port, + I2.input_port: I2.input_port} class TestSampleIterator: diff --git a/tests/composition/test_learning.py b/tests/composition/test_learning.py index a7edd129d15..cdf3c289165 100644 --- a/tests/composition/test_learning.py +++ b/tests/composition/test_learning.py @@ -303,7 +303,7 @@ def test_different_number_of_stimuli_for_targets_and_other_input_mech_error(self assert 'The input dictionary' in error_text assert 'contains input specifications of different lengths ({10, 2})' in error_text or \ 'contains input specifications of different lengths ({2, 10})' in error_text - assert 'The same number of inputs must be provided for each node in a Composition' in error_text + assert 'The same number of inputs must be provided for each receiver in a Composition' in error_text class TestLearningPathwayMethods: From 75bd50e1576eb2ca2ded1217ab523143465a00d7 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 17 Mar 2022 01:29:15 -0400 Subject: [PATCH 188/285] OCM: fix dummy state_feature_function dict --- .../modulatory/control/optimizationcontrolmechanism.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 46146910553..e7d9381dff4 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -2428,7 +2428,8 @@ def _update_state_input_ports_for_controller(self, context=None): for input_port in shadowed_input_ports: input_port_name = f"{SHADOW_INPUT_NAME}{input_port.owner.name}[{input_port.name}]" params = {SHADOW_INPUTS: input_port, - INTERNAL_ONLY:True} + INTERNAL_ONLY:True, + PARAMS: {}} if self.state_feature_function: # Use **state_feature_function** if specified by user in constructor params = self._assign_state_feature_function(params) From cd6f7545e274e1395daead9749f048093f41060c Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 7 Jan 2022 22:15:37 -0500 Subject: [PATCH 189/285] tests: uncomment disabled json tests --- tests/json/test_json.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/json/test_json.py b/tests/json/test_json.py index 09c9f700215..c7a159c1af6 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -18,19 +18,19 @@ json_results_parametrization = [ - # ('model_basic.py', 'comp', '{A: 1}'), - # ('model_nested_comp_with_scheduler.py', 'comp', '{A: 1}'), - # ( - # 'model_with_control.py', - # 'comp', - # '{Input: [0.5, 0.123], reward: [20, 20]}' - # ), + ('model_basic.py', 'comp', '{A: 1}'), + ('model_nested_comp_with_scheduler.py', 'comp', '{A: 1}'), + ( + 'model_with_control.py', + 'comp', + '{Input: [0.5, 0.123], reward: [20, 20]}' + ), ( 'stroop_conflict_monitoring.py', 'Stroop_model', str(stroop_stimuli).replace("'", '') ), - # ('model_backprop.py', 'comp', '{a: [1, 2, 3]}'), + ('model_backprop.py', 'comp', '{a: [1, 2, 3]}'), ] From ec047ad2ca78e1b425e9f504e11663609558f0df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Mar 2022 06:04:04 +0000 Subject: [PATCH 190/285] requirements: update pytest requirement from <7.1.1 to <7.1.2 (#2348) --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 5e11ce032d6..95b05810996 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,5 +1,5 @@ jupyter<=1.0.0 -pytest<7.1.1 +pytest<7.1.2 pytest-benchmark<3.4.2 pytest-cov<3.0.1 pytest-helpers-namespace<2021.12.30 From 8e5d581d49b3a9843f14a2421f855ff321333d0d Mon Sep 17 00:00:00 2001 From: jdcpni Date: Fri, 18 Mar 2022 17:47:40 -0400 Subject: [PATCH 191/285] Refactor/ocm/state features all as input ports (#2350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * see later commit * see later commit * see later commit * see later commit * - This branch passes all tests except: - test_parameterestimationcomposition - test_composition/test_partially_overlapping_control_specs (ADDED IN THIS COMMINT) - All relevant changes to this branch are marked as "11/21/21." However, most are commented out as they break other things. - The tests above both involve local control specifications (on mechanism within a nested comp) and on the OCM for the outer composition, some of which are for the same nested mechs - Both tests fail with: "AttributeError: 'NoneType' object has no attribute '_get_by_time_scale'" (in component.py LINE 3276) This may be due to a problem with context setting, since the error is because the modulation Parameter of the ControlProjection is returning "None" rather than "multiplicative_param" (when called with get(context)), whereas "multiplicative_param" is returned with a call to get() (i.e., with no context specified) - Most of test_partially_overlapping_control_specs is passed if changes marked "11/21/21 NEW" in optimizationcontrolmechanism.py (LINE 1390) are implemented, but it does not properly route ControlProjections through parameter_CIMS (see last assert in test). Furthermore, test_parameterestimationcompsition fails with the mod param error, even though the model has similar structure (i.e., outer composition -- in this case a ParameterEstimationComposition) with an OCM that is given control specs that overlap with ones in a nested composition. - There are also several other things in composition I found puzzling and tried modifying, but that cuased failures: - _get_control_signals_for_composition(): - seems "if node.controller" should be "if **not** node.controller" (emphasis added just for comment) - "append" should be "extend" - _instantiate_control_projection(): - call to self.controller._activate_projections_for_composition (at end of method) should not be indented * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - small mods; don't impact anything relevant to prior commit message * - finished adding formatting regions to composition.py * - * • composition.py: - rename _check_projection_initialization_status -> _check_controller_initialization_status - add _check_nodes_initialization_status(context=context) (and calls it with _check_controller_initialization_status) * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * • show_graph.py: addressed bug associated with ocm.allow_direct_probe * - * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * - * - * - * - * - * - * - * - * • inputport.py: - add figure for shadowing * - * - Co-authored-by: Katherine Mantel Co-authored-by: jdcpni --- .../source/_static/input_port_shadowing_1.svg | 1186 ++++++++ .../source/_static/input_port_shadowing_2.svg | 2480 +++++++++++++++++ .../control/optimizationcontrolmechanism.py | 388 +-- psyneulink/core/components/ports/inputport.py | 47 +- .../core/components/ports/parameterport.py | 2 +- psyneulink/core/compositions/composition.py | 65 +- 6 files changed, 3981 insertions(+), 187 deletions(-) create mode 100644 docs/source/_static/input_port_shadowing_1.svg create mode 100644 docs/source/_static/input_port_shadowing_2.svg diff --git a/docs/source/_static/input_port_shadowing_1.svg b/docs/source/_static/input_port_shadowing_1.svg new file mode 100644 index 00000000000..109d59ba5bb --- /dev/null +++ b/docs/source/_static/input_port_shadowing_1.svg @@ -0,0 +1,1186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputPort-0 + + + + + + + + + + + OutputPorts + + + + + + + + + + + Mechanism + + + + + + + + + + + : + + + + + + + + + + + Mech + + + + + + + + + + + + + + + + + + + + + + + ParameterPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + intercept + + + + + + + + + + + + + + + + + + + + + slope + + + + + + + + + + + + + + + + + + + + + + + InputPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + InputPort-0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputPort-0 + + + + + + + + + + + OutputPorts + + + + + + + + + + + Mechanism + + + + + + + + + + + : + + + + + + + + + + + Shadowed Mech + + + + + + + + + + + + + + + + + + + + + + + ParameterPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + intercept + + + + + + + + + + + + + + + + + + + + + slope + + + + + + + + + + + + + + + + + + + + + + + InputPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + InputPort-0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputPort-0 + + + + + + + + + + + OutputPorts + + + + + + + + + + + Mechanism + + + + + + + + + + + : + + + + + + + + + + + Shadowing Mech + + + + + + + + + + + + + + + + + + + + + + + ParameterPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + intercept + + + + + + + + + + + + + + + + + + + + + slope + + + + + + + + + + + + + + + + + + + + + + + InputPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shadowed input of Shadowed Mech[InputPort-0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/source/_static/input_port_shadowing_2.svg b/docs/source/_static/input_port_shadowing_2.svg new file mode 100644 index 00000000000..fada5f18e1a --- /dev/null +++ b/docs/source/_static/input_port_shadowing_2.svg @@ -0,0 +1,2480 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Outer Composition + + + + + + + + + + + + + + + + + + + + + + + + + Nested Composition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INPUT_CIM_Nested Composition_INPUT_CIM_Mech_InputPort-0 + + + + + + + + + + + + + OutputPorts + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + : + + + + + + + + + + + + + Outer Composition Input_CIM + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INPUT_CIM_Mech_InputPort-0 + + + + + + + + + + + + + OutputPorts + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + : + + + + + + + + + + + + + Nested Composition Input_CIM + + + + + + + + + + + + + + + + + + + + + + + + + + + InputPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INPUT_CIM_Mech_InputPort-0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputPort-0 + + + + + + + + + + + + + OutputPorts + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + : + + + + + + + + + + + + + Shadowing Mech + + + + + + + + + + + + + + + + + + + + + + + + + + + ParameterPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + intercept + + + + + + + + + + + + + + + + + + + + + + + + + slope + + + + + + + + + + + + + + + + + + + + + + + + + + + InputPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shadowed input of Mech[InputPort-0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OutputPort-0 + + + + + + + + + + + + + OutputPorts + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + : + + + + + + + + + + + + + Mech + + + + + + + + + + + + + + + + + + + + + + + + + + + ParameterPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + intercept + + + + + + + + + + + + + + + + + + + + + + + + + slope + + + + + + + + + + + + + + + + + + + + + + + + + + + InputPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + InputPort-0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + : + + + + + + + + + + + + + Outer Composition Output_CIM + + + + + + + + + + + + + + + + + + + + + + + + + + + InputPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OUTPUT_CIM_Nested Composition_OUTPUT_CIM_Mech_OutputPort-0 + + + + + + + + + + + + + + + + + + + + + + + + + OUTPUT_CIM_Shadowing Mech_OutputPort-0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OUTPUT_CIM_Mech_OutputPort-0 + + + + + + + + + + + + + OutputPorts + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + : + + + + + + + + + + + + + Nested Composition Output_CIM + + + + + + + + + + + + + + + + + + + + + + + + + + + InputPorts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OUTPUT_CIM_Mech_OutputPort-0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index e7d9381dff4..410a3d2533b 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -32,7 +32,7 @@ - `Agent Representation ` - `State ` - `Input ` - - `state_input_ports ` + - `state_input_ports ` - `outcome_input_ports ` - `objective_mechanism ` - `monitor_for_control ` @@ -60,17 +60,18 @@ its `agent_rep ` -- a representation of the Composition to be optimized -- under different `control_allocations `, and selecting the one that optimizes its `net_outcome `. A OptimizationControlMechanism can be configured to implement -forms of optimization, ranging from fully `model-based optimization ` +various forms of optimization, ranging from fully `model-based optimization ` that uses the Composition itself as the `agent_rep ` to simulate the outcome for a given `state ` (i.e., a combination of the current input and a particular `control_allocation `), to fully `model-free optimization ` by using a `CompositionFunctionApproximator` as the `agent_rep ` that learns to predict the outcomes for a state. Intermediate forms of -optimization can also be implemented, that use simpler Compositions to approximate the dynamics of the full Composition. -The outcome of executing the `agent_rep ` is used to compute a `net_outcome -` for a given `state `, that takes into account -the `costs ` associated with the `control_allocation, and is used to determine -the optimal `control_allocations `. +optimization can also be implemented, that use simpler Compositions to approximate the dynamics of the full +Composition. The outcome of executing the `agent_rep ` is used to compute a +`net_outcome ` for a given `state `, that takes +into account the `costs ` associated with the `control_allocation +`, and is used to determine the optimal `control_allocations +`. .. _OptimizationControlMechanism_EVC: @@ -79,16 +80,16 @@ The `net_outcome ` of an OptimizationControlMechanism's `agent_rep ` is computed -- for a given `state ` (i.e., set of `state_feature_values ` and a `control_allocation -` -- as the difference between the `outcome ` computed +`) -- as the difference between the `outcome ` computed by its `objective_mechanism ` and the aggregated `costs ` of its `control_signals ` computed by its `combine_costs ` function. If the `outcome ` computed by the `objective_mechanism ` is configured to measure the value of processing (e.g., reward received, time taken to respond, or a combination of these, etc.), and the `OptimizationFunction` assigned as -the OptimizationControlMechanism's `function ` is configured find the +the OptimizationControlMechanism's `function ` is configured to find the `control_allocation ` that maximizes its `net_outcome ` (that is, the `outcome ` discounted by the -result of the `combine_costs ` function, then the OptimizationControlMechanism is +result of the `combine_costs ` function), then the OptimizationControlMechanism is said to be maximizing the `Expected Value of Control (EVC) `_. That is, it implements a cost-benefit analysis that weighs the `costs ` of the ControlSignal `values ` associated with a `control_allocation ` against @@ -122,9 +123,9 @@ ` for a given `state `, and find the `control_allocation ` that optimizes this. The `agent_rep ` can be the `Composition` to which the OptimizationControlMechanism -belongs (and controls), another (presumably simpler) one, or a `CompositionFunctionApproximator`) that is used to -estimate the `net_outcome ` Composition of which the OptimizationControlMechanism is -the `controller `. These different types of `agent representation +belongs (and controls), another (presumably simpler) one, or a `CompositionFunctionApproximator` that is used to +estimate the `net_outcome ` of the Composition of which the OptimizationControlMechanism +is the `controller `. These different types of `agent representation ` correspond closely to the distinction between *model-based* and *model-free* optimization in the `machine learning `_ @@ -149,8 +150,8 @@ *Model-Based Optimization* -The fullest form of this is implemented by assigning as the `agent_rep ` -the Composition for which the OptimizationControlMechanism is the `controller `). +The fullest form of this is implemented by assigning the Composition for which the OptimizationControlMechanism is the +`controller `) as its a`agent_rep ` On each `TRIAL `, that Composition *itself* is provided with either the most recent inputs to the Composition, or ones predicted for the upcoming trial (as determined by the `state_feature_values ` of the OptimizationControlMechanism), and then used to simulate @@ -172,17 +173,17 @@ addressed by use of the term "agent_rep", and how it is implemented, as described below. This clearest form of this uses a `CompositionFunctionApproximator`, that learns to predict the `net_outcome -`net_outcome ` for a given state (e.g., using reinforcement learning or other forms -of function approximation, , such as a `RegressionCFA`). In each `TRIAL ` the `agent_rep +` for a given state (e.g., using reinforcement learning or other forms of +function approximation, such as a `RegressionCFA`). In each `TRIAL ` the `agent_rep ` is used to search over `control_allocation `\\s, to find the one that yields the best predicted `net_outcome ` of processing on the upcoming trial, based on the current or (expected) `state_feature_values ` for that trial. The `agent_rep -` is also given the chance to adapt in order to improve its prediction -of its `net_outcome ` based on the `state `, -and `net_outcome ` of the prior trial. A Composition can also be used to generate -such predictions permitting, as noted above, forms of optimization intermediate between the extreme examples of -model-based and model-free. +` is also given the chance to adapt in order to improve its prediction of +its `net_outcome ` based on the `state ` and +`net_outcome ` of the prior `TRIAL `. A Composition can also be +used to generate such predictions, permitting forms of optimization that are intermediate between the extreme +examples of model-based and model-free, as noted above. .. _OptimizationControlMechanism_Creation: @@ -249,28 +250,35 @@ .. _OptimizationControlMechanism_State_Features_Explicit_Specification: *Explicit specification.* The **state_features** argument can also be specified explicitly, using the formats - described below. This is useful if values other than the `external inputs ` to - the `agent_rep ` Composition are to be used to evaluate it; to restrict - evaluation to a subset of inputs (while others are held constant); and/or to assign specific functions to one or - more `state_input_ports ` that allow them to process the inputs - (e.g., modulate and/or integrate them) before they are assigned as to `state_feature_values - ` (see `below - `). Note that assigning any **state_features** explicitly - overrides their automatic assignment, so that *all* required ones must be specified explicitly, as described below. - Any that are *not* specified will be assigned the value of their `default_variable ` - when the `agent_rep `\\'s `evaluate ` method is - executed. + described below. This is useful if: values other than the `external inputs ` to + the `agent_rep ` Composition are to be used when it is evaluated; to restrict + evaluation to a subset of its inputs (while others are held constant); and/or to assign specific functions to one or + more `state_input_ports ` (see `below + `) that allow them to process the inputs + (e.g., modulate and/or integrate them) before they are assigned to `state_feature_values + `. Note that assigning *any* **state_features** explicitly + overrides their automatic assignment; any that are *not* specified will be assigned the `state_feature_default + `. By default, `state_feature_default + ` is ``None``, which means any InputPort of an `INPUT + ` for which no state_feature is specified will be provided with the value of its `default_variable + ` when the `agent_rep `\\'s + `evaluate ` method is executed. However, `state_feature_default + ` can be set to other values (e.g., SHADOW_INPUTS), that is then + used for any unspecified state_features (e.g., `state_input_ports ` + are constructed that `shadow their inputs `, as in the case of `automatic assignment + `). .. _OptimizationControlMechanism_State_Features_Shapes: .. note:: - If **state_features** are specified explicitly, the shapes of the `value `\\s of the - specified Components must match those required as `external inputs ` to - the corresponding `INPUT ` `Nodes ` of the `agent_rep - `. An example of the input format required by the `INPUT - ` `Nodes ` can be generated using the `agent_rep - `\\'s `get_input_format ` method. - A failure to properly meet these requirements generates an error. + If **state_features** are specified explicitly, the `value `\\s of the specified Components + must match the `input_shape ` of the corresponding InputPorts of the `agent_rep + `\\'s `INPUT ` `Nodes `. Those + InputPorts are listed in the `agent_rep `\\'s + `external_input_ports_of_all_input_nodes ` attribute + and, together with examples of their values, in the OptimizationControlMechanism's `state_feature_values + ` attribute. A failure to properly meet these requirements + produces an error. COMMENT: .. _OptimizationControlMechanism_Selective_Input: @@ -295,18 +303,18 @@ .. _Optimization_Control_Mechanism_State_Feature_Input_Dict: - * *Inputs dictionary* -- a dictionary that conforms to the format used to `specify external inputs - ` to the `agent_rep `, in which entries - consist of a key specifying an `INPUT ` Node of `agent_rep - `, and its value is the source of the input, that can be any of the forms - of individual input specifications listed `below `. - This is the most straightforward and reliable way to specify **state_features**. The full format required for - inputs to `agent_rep ` can be seen using its `get_input_format - ` method. If any `INPUT ` Nodes are not specified or assigned None - as their value, their `default variable ` is used for their input when the - `agent_rep `\\'s `evaluate ` method is executed, - irrespective of the input to the `agent_rep ` during the last `TRIAL - `. If a nested Composition is specified (that is an `INPUT ` Node of `agent_rep + * *Inputs dictionary* -- must conform to the format used to `specify external inputs ` + to the `agent_rep `, in which entries consist of a key specifying an `INPUT + ` Node of `agent_rep `, and its value is the source of the + input, that can be any of the forms of individual input specifications listed `below + `. This is the most straightforward and reliable + way to specify **state_features**. The full format required for inputs to `agent_rep + ` can be seen using its `get_input_format ` + method. If any `INPUT ` Nodes are not specified or assigned None as their value, their `default + variable ` is used for their input when the `agent_rep + `\\'s `evaluate ` method is executed, irrespective + of the input to the `agent_rep ` during the last `TRIAL `. + If a nested Composition is specified (that is an `INPUT ` Node of `agent_rep `), the value assigned to it is used for *all* of the `INPUT ` Nodes for the nested Composition and any nested within it, at all levels of nesting. If one or more `INPUT ` Nodes of a nested Composition (that are INPUT Nodes at all levels of nesting) are @@ -314,42 +322,51 @@ .. _Optimization_Control_Mechanism_State_Feature_List_Inputs: - * *List* -- a list of individual input source specifications, that can be any of the forms of individual input - specifications listed `below `. The items - must be listed in the order that `INPUT ` Nodes are listed in the `agent_rep - `\\'s `nodes ` attribute (and returned by its - `get_nodes_by_role(NodeRole.INPUT) ` method). If the list is incomplete, - the remaining INPUT Nodes are assigned their `default variable ` as input when the `agent_rep - `\\'s `evaluate ` method is called; ``None`` can - be used as an entry to "skip" items in the list (i.e., specify that they receive their `default variable - ` as input). Items can be specified in the list that have not yet been added to the + * *List* -- a list of individual input source specifications, that can be any of the forms of individual + input specifications listed `below `. + The items correspond to all of the external InputPorts (i.e., ones not designated as `internal_only + `) of all of the `INPUT ` Nodes` of the `agent_rep + `, and must be specified in the order they are listed in the `agent_rep + `\\'s `external_input_ports_of_all_input_nodes + ` attribute. If the list is incomplete, the remaining + InputPorts are assigned the `state_feature_default `; + ``None`` can also be used as an entry in the list to "skip" that item (i.e., specify that it is assigned the + `state_feature_default `. If the `state_feature_default + ` is ``None`` (*its* default value), then no `state_input_port + ` is constructed for that source, and the InputPort of the + `INPUT ` `Node ` will be assigned the value of its `default variable + ` as its input when the `agent_rep `\\'s `evaluate + ` method is executed. Items can be included in the list that have not yet been added to the OptimizationControlMechanism's Composition or its `agent_rep `, that are either sources of input to `agent_rep `\\'s `INPUT ` - `Nodes `, or those Nodes themselves. However, these must be added before Composition is - executed, and must appear in the list in the same position that the `INPUT Nodes to which they pertain are - list in the `agent_rep `\\'s `nodes ` attribute. + `Nodes `, or those Nodes themselves. However, these must be added before the Composition is + executed, and must appear in the list in the same position that the InputPorts to which they pertain are listed + in the `agent_rep `\\'s `external_input_ports_of_all_input_nodes + ` attribute, once construction of the `agent_rep + ` is complete. .. _Optimization_Control_Mechanism_State_Feature_Set_Inputs: * *Set* -- a set of `INPUT ` `Nodes ` of the `agent_rep - ` to receive the same inputs during evaluation as when - the `agent_rep ` is fully executed; the `state_input_ports - ` constructed for these state_features are assigned - Projections that `shadow ` the specified `INPUT ` `Node + ` to receive the same inputs during evaluation as when the `agent_rep + ` is fully executed; the `state_input_ports + ` constructed for these state_features are assigned Projections + that `shadow ` the inputs to the specified `INPUT ` `Nodes ` of the `agent_rep `. The order of their specification does not matter; however, any of the `agent_rep `\\'s - `INPUT ` Nodes that are *not* included in the set will be assigned their `default variable - ` when the `agent_rep `\\'s `evaluate - ` method is called. + `INPUT ` Nodes that are *not* included in the set will be assigned the `state_feature_default + ` if it is specified; otherwise, they are assigned their + `default variable ` when the `agent_rep `\\'s + `evaluate ` method is executed. .. _Optimization_Control_Mechanism_State_Feature_Individual_Inputs: * *Individual inputs* -- any of the forms below can be used singly, or in a dict or list as described `above `, to configure a `state_input_port - `, the value of which is assigned as the corresponding element - of `state_feature_values ` provided as input to the `INPUT - ` `Node ` of the `agent_rep ` when it - is `evaluated ` method is called. + `, the value of which is assigned as the corresponding entry of + `state_feature_values ` and provided as input to the + corresponding InputPort of the `agent_rep `\\'s `INPUT ` + `Nodes ` when it's `evaluate ` method is executed. .. note:: If only a single input specification is provided to **state_features**, it is treated as a list with a single @@ -366,6 +383,11 @@ input to the corresponding `INPUT ` `Node ` of the `agent_rep ` each time it is `evaluated `. + .. _Optimization_Control_Mechanism_SHADOW_INPUTS_State_Feature: + + * *SHADOW_INPUTS* -- create an `InputPort` that `shadows the input ` of the InputPort + to which the specification is assigned. + .. _Optimization_Control_Mechanism_Tuple_State_Feature: * *2-item tuple* -- the first item must be a `Port` or `Mechanism` specification, as described below; @@ -386,7 +408,7 @@ .. _Optimization_Control_Mechanism_Input_Port_State_Feature: * *InputPort specification* -- creates an `InputPort` that `shadows ` the input to - the specified InputPort, the `value ` of which is used as the corresponding value of the + the specified InputPort, the `value ` of which is assigned as the corresponding value of the OptimizationControlMechanism's `state_feature_values `. .. note:: @@ -405,7 +427,7 @@ the OptimizationControlMechanism's constructor (see below). .. technical_note:: - The InputPorts specified as state_features are marked as `internal_only ` = `True`. + The InputPorts specified as state_features are designated as `internal_only ` = `True`. .. _Optimization_Control_Mechanism_Output_Port_State_Feature: @@ -456,11 +478,6 @@ `shadowed `, then its InputPort must be specified explicitly (as described `above `). - COMMENT: - FIX: CONFIRM THAT THE FOLLOWING WORKS - State features can also be added to an existing OptimizationControlMechanism using its `add_state_features` method. - COMMENT - | .. _OptimizationControlMechanism_State_Feature_Function_Arg: @@ -580,7 +597,7 @@ The current state of the OptimizationControlMechanism -- or, more properly, of its `agent_rep ` -- is determined by the OptimizationControlMechanism's current `state_feature_values ` (see `below -`) and `control_allocation `. +`) and `control_allocation `. These are used by the `evaluate_agent_rep ` method, the results of which are combined with the `costs ` associated with the `control_allocation `, to evaluate the `net_outcome @@ -602,7 +619,7 @@ ` for the `control_allocation ` under which the execution occurred. Each of these is described below. -.. _OptimizationControlMechanism_State_Features: +.. _OptimizationControlMechanism_State_Input_Ports: *state_input_ports* ~~~~~~~~~~~~~~~~~~~ @@ -613,12 +630,14 @@ `, and conveyed to the `agent_rep ` when it is `executed `. If the `agent_rep is a Composition `, then the -OptimizationControlMechanism has a state_input_port for every `InputPort` of every `INPUT ` `Node -` of the `agent_rep ` Composition, each of which receives -a `Projection` that `shadows the input ` of the corresponding state_feature. If the -`agent_rep is a CompositionFunctionApproximator `, -then the OptimizationControlMechanism has a state_input_port that receives a Projection from each Component specified -in the **state_features** arg of its constructor. +OptimizationControlMechanism has a state_input_port for every specification in the **state_features** arg of its +contructor (see `above `) or, if none are specified, then a +state_input_port is constructed for every `InputPort` of every `INPUT ` `Node ` +of `agent_rep ` Composition, each of which receives a `Projection` that +`shadows the input ` of those InputPorts. If the `agent_rep is a +CompositionFunctionApproximator `, then the OptimizationControlMechanism +has a state_input_port that receives a Projection from each Component specified in the **state_features** arg of its +constructor. COMMENT: In either, case the the `values ` of the @@ -630,15 +649,15 @@ State features can be of two types: -* *Input Features* -- these are values that shadow the input received by a `Mechanisms ` in the - `Composition` for which the OptimizationControlMechanism is a `controller ` (irrespective - of whether that is the OptimizationControlMechanism`s `agent_rep `). - They are implemented as `shadow InputPorts ` (see - `OptimizationControlMechanism_State_Features_Shadow_Inputs` for specification) that receive a - `Projection` from the same source as the Mechanism being shadowed. +* *Input Features* -- these are values that either shadow the input received by an `InputPort` of a `Mechanisms + ` in the `Composition` for which the OptimizationControlMechanism is a `controller + ` (irrespective of whether that is the OptimizationControlMechanism`s `agent_rep + `). They are implemented as `shadow InputPorts ` + (see `OptimizationControlMechanism_State_Features_Shadow_Inputs` for specification) that receive a `Projection` + from the same source as the Mechanism being shadowed. .. -* *Output Features* -- these are the `value ` of an `OutputPort` of `Mechanism ` in the - `Composition` for which the OptimizationControlMechanism is a `controller ` (again, +* *Output Features* -- these are the `value ` of an `OutputPort` of a `Mechanism ` in + the `Composition` for which the OptimizationControlMechanism is a `controller ` (again, irrespective of whether it is the OptimizationControlMechanism`s `agent_rep `); and each is assigned a `Projection` from the specified OutputPort(s) to the InputPort of the OptimizationControlMechanism for that feature. @@ -1149,6 +1168,12 @@ class OptimizationControlMechanism(ControlMechanism): 's `evaluate ` method when it is executed. See `state_features ` for details of specification. + state_feature_default : same as state_features : default None + specifies the default used if a state_feature is not otherwise specified for the `InputPort` of an + `INPUT ` `Node ` of `agent_rep `. + (see `state_feature_default ` and + `state_features ` for additional details). + state_feature_function : Function or function : default None specifies the `function ` to use as the default function for the `state_input_ports ` created for the corresponding **state_features** (see @@ -1252,7 +1277,7 @@ class OptimizationControlMechanism(ControlMechanism): values of which are assigned to `state_feature_values ` and provided as input to the `agent_rep 's `evaluate ` method when it is executed (see `state_features ` and - `OptimizationControlMechanism_State_Features` for additional details). + `OptimizationControlMechanism_State_Input_Ports` for additional details). .. technical_note:: the state_features dict is used by the _build_predicted_inputs() method of an `agent_rep @@ -1267,10 +1292,23 @@ class OptimizationControlMechanism(ControlMechanism): in either `agent_rep ` and/or of any Compositions nested at any level within it. + state_feature_default : Mechanism, InputPort, OutputPort, Projection, dict, SHADOW_INPUTS, numeric value + determines the default used if the state_feature (i.e. source) is not otherwise specified for the `InputPort`of + an `INPUT ` `Node ` of `agent_rep `. + If it is None, then no corresponding `state_input_port ` + is created for that InputPort, and its `default variable ` is used as its input when the + `agent_rep `\\'s `evaluate ` method is executed + (see `state_features ` for additional details). + state_feature_values : 2d array - the current value of each item of the OptimizationControlMechanism's `state_input_ports - (see `OptimizationControlMechanism_State_Features` - for additional details). + a dict containing the current values assigned as the input to the InputPorts of the `INPUT ` + `Nodes ` of the `agent_rep ` when its `evaluate + ` method is executed. For each such InputPort, if a `state_feature + ` has been specified for it, then its value in + state_feature_values is the `value ` of the corresponding `state_input_port + `; otherwise the InputPort's `default_variable + ` is assigned as its value in state_feature_values (see + `OptimizationControlMechanism_State_Input_Ports` for additional details). state_feature_function : Function of function determines the `function ` used as the default function for @@ -1283,7 +1321,7 @@ class OptimizationControlMechanism(ControlMechanism): or constructed automatically (see `state_features `), the values of which are assigned to `state_feature_values ` and provided as input to the `agent_rep 's `evaluate - ` method (see `OptimizationControlMechanism_State_Features` for additional details). + ` method (see `OptimizationControlMechanism_State_Input_Ports` for additional details). num_state_input_ports : int contains the number of `state_input_ports `. @@ -1545,12 +1583,18 @@ class Parameters(ControlMechanism.Parameters): :default value: None :type: - state_features - see `state_features ` + state_feature_specs + see `state_feature_specs ` :default value: None :type: ``dict`` + state_feature_default + see `state_feature_default ` + + :default value: None + :type: + state_feature_function see `state_feature_function ` @@ -1563,11 +1607,15 @@ class Parameters(ControlMechanism.Parameters): :default value: None :type: ``list`` """ - outcome_input_ports_option = Parameter(CONCATENATE, stateful=False, loggable=False, structural=True) + agent_rep = Parameter(None, stateful=False, loggable=False, pnl_internal=True, structural=True) state_input_ports = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) state_feature_specs = Parameter(None, stateful=False, loggable=False, read_only=True, structural=True, parse_spec=True) + state_feature_default = Parameter(None, stateful=False, loggable=False, read_only=True) state_feature_function = Parameter(None, reference=True, stateful=False, loggable=False) + state_feature_values = Parameter(None,getter=_state_feature_values_getter, + user=False, pnl_internal=True, read_only=True) + outcome_input_ports_option = Parameter(CONCATENATE, stateful=False, loggable=False, structural=True) function = Parameter(GridSearch, stateful=False, loggable=False) search_function = Parameter(None, stateful=False, loggable=False) search_space = Parameter(None, read_only=True) @@ -1575,11 +1623,6 @@ class Parameters(ControlMechanism.Parameters): comp_execution_mode = Parameter('Python', stateful=False, loggable=False, pnl_internal=True) search_statefulness = Parameter(True, stateful=False, loggable=False) - agent_rep = Parameter(None, stateful=False, loggable=False, pnl_internal=True, structural=True) - - state_feature_values = Parameter(None,getter=_state_feature_values_getter, - user=False, pnl_internal=True, read_only=True) - # FIX: Should any of these be stateful? random_variables = ALL initial_seed = None @@ -1608,12 +1651,21 @@ def _parse_state_feature_specs(self, state_features): or SHADOW_INPUTS in state_features))) else convert_to_list(state_features)) + def _validate_state_feature_default(self, state_feature_default): + # FIX: 3/16/22 - from psyneulink.core.compositions.composition import Composition + if not (isinstance(state_feature_default, (InputPort, OutputPort, Mechanism)) + or state_feature_default == SHADOW_INPUTS + or is_numeric(state_feature_default) + or state_feature_default is None): + return f"must be an InputPort, OutputPort, Mechanism, Composition, SHADOW_INPUTS or a list or array " \ + f"with a shape appropriate for all of the INPUT Nodes or InputPorts to which it will be applied." + @handle_external_context() @tc.typecheck def __init__(self, agent_rep=None, state_features: tc.optional(tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort)))=None, - default_state_feature=None, + state_feature_default=None, state_feature_function: tc.optional(tc.optional(tc.any(dict, is_function_type)))=None, function=None, num_estimates=None, @@ -1691,6 +1743,7 @@ def __init__(self, super().__init__( agent_rep=agent_rep, state_feature_specs=state_features, + state_feature_default=state_feature_default, state_feature_function=state_feature_function, function=function, num_estimates=num_estimates, @@ -1743,7 +1796,6 @@ def _validate_params(self, request_set, target_set=None, context=None): f"do not match any InputPorts specified in its " f"{STATE_FEATURES} arg: {invalid_fct_specs}.") - if self.random_variables is not ALL: invalid_params = [param.name for param in self.random_variables if param not in self.agent_rep.random_variables] @@ -1764,7 +1816,7 @@ def _instantiate_input_ports(self, context=None): The OptimizationControlMechanism's outcome_input_ports are instantiated by ControlMechanism._instantiate_input_ports in the call to super(). - InputPorts are constructed for **state_features** by calling _parse_state_feature_specs + InputPorts are constructed for **state_features** by calling _complete_parsing_state_feature_specs with them and **state_feature_function** arguments of the OptimizationControlMechanism constructor. The constructed state_input_ports are passed to ControlMechanism_instantiate_input_ports(), which appends them to the InputPort(s) that receive input from the **objective_mechanism* (if specified) @@ -1779,14 +1831,14 @@ def _instantiate_input_ports(self, context=None): (handled in _update_state_input_ports_for_controller). See `state_features ` and - `OptimizationControlMechanism_State_Features` for additional details. + `OptimizationControlMechanism_State_Input_Ports` for additional details. """ # If any state_features were specified parse them and pass to ControlMechanism._instantiate_input_ports() state_input_ports_specs = None # FIX: 11/3/21 : - # ADD CHECK IN _parse_state_feature_specs THAT IF A NODE RATHER THAN InputPort IS SPECIFIED, + # ADD CHECK IN _complete_parsing_state_feature_specs THAT IF A NODE RATHER THAN InputPort IS SPECIFIED, # ITS PRIMARY IS USED (SEE SCRATCH PAD FOR EXAMPLES) if not self.state_feature_specs: # If agent_rep is CompositionFunctionApproximator, warn if no state_features specified. @@ -1797,7 +1849,7 @@ def _instantiate_input_ports(self, context=None): else: # Implement any specified state_features - state_input_ports_specs = self._parse_state_feature_specs(context) + state_input_ports_specs = self._complete_parsing_state_feature_specs(context) # Note: # if state_features were specified and agent_rep is a CompositionFunctionApproximator, # assume they are OK (no way to check their validity for agent_rep.evaluate() method, and skip assignment @@ -1889,7 +1941,7 @@ def _validate_input_nodes(self, nodes, enforce=None): # FIX: 1/29/22 - REFACTOR TO SUPPORT TUPLE AND InportPort SPECIFICATION DICT FOR MULT. PROJS. TO STATE_INPUT_PORT # FIX: 2/25/22 - REFACTOR TO SUPPORT InputPort SPECIFICATIONS IN dict, set and list specs - def _parse_state_feature_specs(self, context=None): + def _complete_parsing_state_feature_specs(self, context=None): """Parse entries of state_features specifications used to construct state_input_ports. Called from _instantiate_input_ports() @@ -2012,7 +2064,6 @@ def expand_nested_input_comp_to_input_nodes(comp): # PARSE SPECS ------------------------------------------------------------------------------------------ # Generate parallel lists of INPUT Nodes and corresponding feature specs (for sources of inputs) - def _parse_specs(state_feature_specs, spec_str="list"): """Validate and parse INPUT Node specs assigned to construct state_feature_specs Validate number and identity of specs relative to agent_rep INPUT Nodes. @@ -2070,48 +2121,61 @@ def _parse_specs(state_feature_specs, spec_str="list"): f"({comp_names}) in the {spec_str} specified for its '{STATE_FEATURES}' argument; these must be " f"replaced by direct references to the Mechanisms (or their InputPorts) within them to be " f"shadowed.") + spec_names = [] num_specs = len(state_feature_specs) num_ports = len(agent_rep_input_ports) self._num_state_feature_specs = max(num_specs, num_ports) for i in range(self._num_state_feature_specs): + # PORT & PORT_NAME - # (and specs for CFA and any nodes not yet in agent_rep) + # (and specs for CFA and any Nodes not yet in agent_rep) spec_name = None state_feature_fct = None if self.agent_rep_type == COMPOSITION: + # Process number of specs for which there are known INPUT Ports of agent_rep if i < num_ports: + # Just get port and port name (spec will be parsed and assigned below) # Node should be in agent_rep, so use that to be sure if self.agent_rep_type == COMPOSITION: - node = agent_rep_input_ports[i] - node_name = node.full_name + port = agent_rep_input_ports[i] + port_name = port.full_name + # For Nodes not (yet) in agent_rep, so else: - # Node not (yet) in agent_rep, so "DEFERRED n" as node name + # - get specified value for spec, for later parsing and assignment (once Node is known) spec = state_feature_specs[i] - node = None - node_name = f'DEFFERED {str(i-num_ports)}' - # Assign spec as node for CompositionFunctionApproximator + port = None + # - assign "DEFERRED n" as node name + port_name = f'DEFFERED {str(i-num_ports)}' + # For CompositionFunctionApproximator, assign spec as port else: spec = state_feature_specs[i] - node = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner - node_name = f"FEATURE {i} FOR {self.agent_rep.name}" + port = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner + port_name = f"FEATURE {i} FOR {self.agent_rep.name}" # SPEC - # Assign specs - # Only process specs for which there are already INPUT Nodes in agent_rep + # Pare and assign specs for INPUT Nodes already in agent_rep (i.e., unassigned above) # (others may be added to Composition later) if i < num_specs: - spec = state_feature_specs[i] + + # Assign either state_feature_specs[i] or self.state_feature_default if specified, else None + if state_feature_specs[i] is not None: + spec = state_feature_specs[i] + elif self.state_feature_default is not None: + spec = self.state_feature_default + else: + spec = None + # Assign input_port name if is_numeric(spec): - spec_name = f"{node_name} {DEFAULT_VARIABLE.upper()}" + spec_name = f"{port_name} {DEFAULT_VARIABLE.upper()}" elif isinstance(spec, (Port, Mechanism, Composition)): if hasattr(spec, 'full_name'): spec_name = spec.full_name else: spec_name = spec.name elif isinstance(spec, dict): - spec_name = spec[NAME] if NAME in spec else f"STATE FEATURE INPUT for {node_name}" + spec_name = spec[NAME] if NAME in spec else f"STATE FEATURE INPUT for {port_name}" if FUNCTION in spec: state_feature_fct = spec[FUNCTION] elif PARAMS in spec and FUNCTION in spec[PARAMS]: @@ -2119,16 +2183,19 @@ def _parse_specs(state_feature_specs, spec_str="list"): elif isinstance(spec, tuple): state_feature_fct = spec[1] spec = spec[0] + elif spec == SHADOW_INPUTS: + spec = port elif spec is not None: assert False, f"PROGRAM ERROR: unrecognized form of state_feature specification for {self.name}" + + # Fewer specifications than number of INPUT Nodes, + # the remaining ones may be specified later, but for now assume they are meant to be ignored else: - # Fewer specifications than number of INPUT Nodes, - # the remaining ones may be specified later, but for now assume they are meant to be ignored spec = None parsed_feature_specs.append(spec) self._state_feature_functions.append(state_feature_fct) - self._specified_INPUT_Node_InputPorts_in_order.append(node) + self._specified_INPUT_Node_InputPorts_in_order.append(port) spec_names.append(spec_name) self.parameters.state_feature_specs.set(parsed_feature_specs, override=True) @@ -2407,6 +2474,8 @@ def _update_state_input_ports_for_controller(self, context=None): self._validate_state_features(context) return + # FIX: 3/18/22 - ??MOVE THIS TO _complete_parsing_state_feature_specs() + # SHOULD NEVER BE THE CASE HERE?? elif not self.state_input_ports: # agent_rep is Composition, but no state_features have been specified, # so assign a state_input_port to shadow every InputPort of every INPUT node of agent_rep @@ -2461,10 +2530,11 @@ def _validate_state_features(self, context): - and/or in run() as final check before execution. Ensure that: - - if state_feature_specs are speified as a user dict, keys are valid INPUT Nodes of agent_rep; + - the number of state_feature_specs equals the number of external InputPorts of INPUT Nodes of agent_rep; + - if state_feature_specs are specified as a user dict, keys are valid INPUT Nodes of agent_rep; - all InputPorts shadowed by specified state_input_ports are in agent_rep or one of its nested Compositions; - any Projections received from output_ports are from Nodes in agent_rep or its nested Compositions; - - all InputPorts shadowed by state_input_ports reference INPUT Nodes of agent_rep or of a nested Composition; + - all InputPorts shadowed by state_input_ports reference INPUT Nodes of agent_rep or Compositions nested in it; - state_features are compatible with input format for agent_rep Composition """ @@ -2472,23 +2542,28 @@ def _validate_state_features(self, context): from psyneulink.core.compositions.composition import \ Composition, CompositionInterfaceMechanism, CompositionError, RunError, NodeRole + # FIX: 3/17/22 - WHY NOT JUST USE: + # self.parameters.state_feature_specs AND + # self._specified_INPUT_Node_InputPorts_in_order[i] + comp = self.agent_rep user_specs = self.parameters.state_feature_specs.spec # user_specs = self.state_feature_specs + # FOR DEBUGGING: + self.state_feature_specs + self._specified_INPUT_Node_InputPorts_in_order + assert len(self.state_feature_specs) == len(self._specified_INPUT_Node_InputPorts_in_order) + # ------------- + if isinstance(user_specs, dict) and SHADOW_INPUTS in user_specs: state_feature_specs = user_specs[SHADOW_INPUTS] else: state_feature_specs = user_specs - if isinstance(state_feature_specs, list): - # FIX: 2/20/22: CONSIDER ALLOWING PARTIAL SPECIFICATION OF INPUT NODES IN NESTED COMPS: - # NEED TO DETERMINE WHETHER NODE IS NESTED AND, IF SO, AND ONLY PARTIAL - # FLESH OUT SPEC FOR REST OF NEST COMP - # Convert list to dict, assuming list is in order of INPUT Nodes, - # and assigning the corresponding INPUT Nodes as keys for use in comp._build_predicted_inputs_dict() - input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) - if len(state_feature_specs) > len(input_nodes): + # Convert list to dict (assuming list is in order of InputPorts of INPUT Nodes) + input_ports = comp.external_input_ports_of_all_input_nodes + if len(state_feature_specs) > len(input_ports): nodes_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" for spec in self._get_specs_not_in_agent_rep(state_feature_specs)] missing_nodes_str = (f", that includes the following: {', '.join(nodes_not_in_agent_rep)} " @@ -2496,11 +2571,11 @@ def _validate_state_features(self, context): if nodes_not_in_agent_rep else '') raise OptimizationControlMechanismError( f"The number of '{STATE_FEATURES}' specified for {self.name} ({len(state_feature_specs)}) " - f"is more than the number of INPUT Nodes ({len(input_nodes)}) of the Composition assigned " + f"is more than the number of INPUT Nodes ({len(input_ports)}) of the Composition assigned " f"as its {AGENT_REP} ('{self.agent_rep.name}'){missing_nodes_str}.") input_dict = {} for i, spec in enumerate(state_feature_specs): - input_dict[input_nodes[i]] = spec + input_dict[input_ports[i]] = spec state_features = state_feature_specs elif isinstance(state_feature_specs, (set, dict)): # If set or dict is specified, check that items of set or keys of dict are legal INPUT nodes: @@ -3376,20 +3451,3 @@ def _initialize_composition_function_approximator(self, context): self.agent_rep.initialize(features_array=np.array(self.defaults.variable[1:]), control_signals = self.control_signals, context=context) - - # # FIX: NEEDS TO BE UPDATED / REFACTORED TO WORK WITH _parse_state_feature_specs - # # FIX: THE FOLLOWING SHOULD BE MERGED WITH HANDLING OF PredictionMechanisms FOR ORIG MODEL-BASED APPROACH; - # # FIX: SHOULD BE GENERALIZED AS SOMETHING LIKE update_feature_values - # @tc.typecheck - # @handle_external_context() - # def add_state_features(self, features, context=None): - # """Add InputPorts and Projections to OptimizationControlMechanism for state_features used to - # predict `net_outcome ` - # - # **state_features** argument can use any of the forms of specification allowed for InputPort(s) - # """ - # - # if features: - # features = self._parse_state_feature_specs(features=features, - # context=context) - # self.add_ports(InputPort, features) diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 0771c92971d..d8a0b6ed816 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -327,13 +327,15 @@ one(s) specified (see below). For each InputPort specified, and all of the InputPorts belonging to any Mechanisms specified, a new InputPort - is created along with Projections to it that parallel those received by the corresponding InputPort in the - list. In other words, for each InputPort specified, a new one is created that receives exactly the same inputs - from the same `senders ` as the ones specified. + is created along with Projections to it that parallel those received by the corresponding InputPort in the list. + That is, for each InputPort specified, one is created that receives exactly the same inputs from the same `senders + ` as the ones specified (see `examples ` below). If an InputPort shadows another, its `shadow_inputs ` attribute identifies the InputPort that it shadows. + .. _InputPort_Shadow_Nested_Note: + .. note:: Only InputPorts belonging to Mechanisms in the *same Composition*, or ones that are `INPUT ` `Nodes ` of a `nested ` can be specified for shadowing. Note also that @@ -350,6 +352,45 @@ for that Mechanism; c) use that Mechanism as the `InputPort specification ` for the shadowing InputPort. + .. _InputPort_Shadow_Inputs_Figures: + + **Examples of InputPort Shadowing** + + .. figure:: _static/input_port_shadowing_1.svg + :scale: 50 % + :alt: Simple Shadowing + + **Simple case of shadowing**. The figure above shows a simple case in which ``Shadowing Mech`` is configured + to shadow the input to ``Shadowed Mech``, as specified below:: + + >>> A = ProcessingMechanism(name='Mech') + >>> B = ProcessingMechanism(name='Shadowed Mech') + >>> C = ProcessingMechanism(name='Shadowing Mech', input_ports=[B.input_port]) + >>> ocomp = Composition(pathways=[[A, B], C], + ... show_graph_attributes={'direction':'LR'}) + >>> ocomp.show_graph(show_node_structure=True) + + .. figure:: _static/input_port_shadowing_2.svg + :alt: Shadowing Nested Mechanism + + **Shadowing a nested Mechanism**. This example shows a Composition in which the `InputPort` of + ``shadowing_mech`` is configured to shadow the input to ``mech`` in ``nested_comp``. Accordingly + ``shadowing_mech`` receives a Projection from the same Port of ``outer_comp``'s `input_CIM + ` as the `input_CIM ` of ``nested_comp`` that projects to + ``mech``. As a result, ``shadowing_mech`` will receive the same input as ``mech`` when ``outer_comp`` + is executed (as noted `above `, only the `INPUT ` `Nodes + ` of a `nested Composition ` can be shadowed):: + + >>> import psyneulink as pnl + >>> mech = pnl.ProcessingMechanism(name='Mech') + >>> shadowing_mech = pnl.ProcessingMechanism(name='Shadowing Mech', + ... input_ports=[mech.input_port]) + >>> nested_comp = pnl.Composition([mech], name='Nested Composition') + >>> outer_comp = pnl.Composition(nodes=[nested_comp, shadowing_mech], + ... name='Outer Composition') + ... show_graph_attributes={'direction':'LR'}) + >>> outer_comp.show_graph(show_node_structure=True, show_cim=True) + .. _InputPort_Compatability_and_Constraints: InputPort `variable `: Compatibility and Constraints diff --git a/psyneulink/core/components/ports/parameterport.py b/psyneulink/core/components/ports/parameterport.py index 455a4ca4742..c37514c3f58 100644 --- a/psyneulink/core/components/ports/parameterport.py +++ b/psyneulink/core/components/ports/parameterport.py @@ -971,7 +971,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 self.source._get(context) - def get_label(self, context): + def get_label(self, context=None): raise ParameterPortError(f"{ParameterPort.__name__}s do not have labels.") @property diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index eb3f7784d5f..dddd1d17624 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -3503,20 +3503,28 @@ class Composition(Composition_Base, metaclass=ComponentsMeta): `, then from any `Nodes ` in the outer composition that project to the nested Composition (either itself, as a Node in the outer Composition, or to any of its own Nodes). - external_input_shape : list[InputPort] - a list of the `external_input_shape `\\s of all of the `InputPorts ` - of the Composition's `INPUT ` `Nodes ` (corresponding to the external - InputPots of its `input_CIM `); any input to the Composition must be compatible with the shape of - this, whether received from the **inputs** argument of one of the Composition's`execution methods - ` or, if it is a `nested Composition `, from the outer - Composition. - - external_input_variables : list[InputPort] - a list of the values of associated with the `InputPorts ` listed in `external_input_ports - `; any input to the Composition must be compatible with the shape of this, - whether received from the **input_ports** argument of oneo f the Composition's`execution methods - ` or, if it is a `nested Composition `, from the outer - Composition. + external_input_ports_of_all_input_nodes: list[InputPort] + a list of all external InputPorts (i.e., ones not desginated `internal_only `) of + all `INPUT ` `Nodes ` of the Composition, including any that in `nested + Compositions ` within it (i.e., within `INPUT ` Nodes at all levels of + nesting) Note that the InputPorts listed are those of the actual Mechanisms projected to by the ones listed + in `external_input_ports `. + + external_input_shape : list[1d array] + a list of the `input_shape `\\s of all of the InputPorts listed in `external_input_ports + ` (and are the same as the shapes of those listed in + `external_input_ports_of_all_input_nodes `); any input + to the Composition must be compatible with these, whether received from the **inputs** argument of one of the + Composition's`execution methods ` or, if it is a `nested Composition + `, from the enclosing Composition. + + external_input_variables : list[2d array] + a list of the `variable `\\s associated with the `InputPorts ` listed in + `external_input_ports `. + + external_input_values : list[1d array] + a list of the values of associated of the `InputPorts ` listed in `external_input_ports + `. external_input_values : list[InputPort] a list of the values of associated with the `InputPorts ` listed in `external_input_ports @@ -4336,6 +4344,12 @@ def _get_input_receivers(self, - True, include the nested Composition but not its INPUT Nodes - ALL, include the nested Composition AND its INPUT Nodes """ + + # FIX: 3/16/22: + # CAN THIS BE REPLACED BY: + # return [self._get_destination(output_port.efferents[0])[0] + # for _,output_port in self.input_CIM.port_map.values()] + assert not (type == PORT and comp_as_node), f"PROGRAM ERROR: _get_input_receivers() can't be called " \ f"for 'ports' and 'nodes' at the same time." input_items = [] @@ -11538,12 +11552,25 @@ def simulation_results(self): @property def external_input_ports(self): - """Returns all external InputPorts that belong to the Input CompositionInterfaceMechanism""" + """Return all external InputPorts that belong to the Input CompositionInterfaceMechanism""" try: return [input_port for input_port in self.input_CIM.input_ports if not input_port.internal_only] except (TypeError, AttributeError): return None + @property + def external_input_ports_of_all_input_nodes(self): + """Return all external InputPorts of all INPUT Nodes (including nested ones) of Composition. + Note: the InputPorts returned are those of the actual Mechanisms + to which the ones returned by external_input_ports ultimately project. + """ + try: + # return [self._get_destination(output_port.efferents[0])[0] + # for _,output_port in self.input_CIM.port_map.values()] + return self._get_input_receivers(comp=self, type=PORT, comp_as_node=False) + except (TypeError, AttributeError): + return None + @property def external_input_shape(self): """Alias for _default_external_input_shape""" @@ -11551,7 +11578,7 @@ def external_input_shape(self): @property def _default_external_input_shape(self): - """Returns default_input_shape of all external InputPorts that belong to Input CompositionInterfaceMechanism""" + """Return default_input_shape of all external InputPorts that belong to Input CompositionInterfaceMechanism""" try: return [input_port.default_input_shape for input_port in self.input_CIM.input_ports # FIX: 2/4/22 - IS THIS NEEDED (HERE OR BELOW -- DO input_CIM.input_ports EVER GET ASSIGNED THIS? @@ -11563,7 +11590,8 @@ def _default_external_input_shape(self): def external_input_variables(self): """Return variables of all external InputPorts that belong to the Input CompositionInterfaceMechanism""" try: - return [input_port.variable for input_port in self.input_CIM.input_ports if not input_port.internal_only] + return [input_port.parameters.variable.get() + for input_port in self.input_CIM.input_ports if not input_port.internal_only] except (TypeError, AttributeError): return None @@ -11583,7 +11611,8 @@ def external_input_values(self): """Return values of all external InputPorts that belong to the Input CompositionInterfaceMechanism""" try: # FIX: 2/4/22 SHOULD input_port.variable REPLACE input_port.value HERE? - return [input_port.value for input_port in self.input_CIM.input_ports if not input_port.internal_only] + return [input_port.parameters.value.get() + for input_port in self.input_CIM.input_ports if not input_port.internal_only] except (TypeError, AttributeError): return None From 14381f29096803c13102213c11c1cba74eab8f2a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 21 Sep 2021 01:04:28 -0400 Subject: [PATCH 192/285] ControlMechanism: label monitor_for_control as structural --- .../components/mechanisms/modulatory/control/controlmechanism.py | 1 + 1 file changed, 1 insertion(+) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index 117b7b49608..3da410a7ae5 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -1205,6 +1205,7 @@ class Parameters(ModulatoryMechanism_Base.Parameters): stateful=False, loggable=False, read_only=True, + structural=True, ) output_ports = Parameter( From 62aa8960fab49ebe17d3a215ed94c78d9f84bf94 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Sat, 8 May 2021 00:57:41 -0400 Subject: [PATCH 193/285] JSON: fix handling string as Condition argument --- psyneulink/core/globals/json.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 9f7dcc39571..751f7370010 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -594,18 +594,15 @@ def _generate_scheduler_string( def _generate_condition_string(condition_dict, component_identifiers): def _parse_condition_arg_value(value): - pnl_str = parse_string_to_psyneulink_object_string(value) try: identifier = parse_valid_identifier(value) except TypeError: - identifier = None - - if identifier in component_identifiers: - return identifier - elif pnl_str is not None: - return f'psyneulink.{pnl_str}' + pass else: - return str(value) + if identifier in component_identifiers: + return str(identifier) + + return str(_parse_parameter_value(value, component_identifiers)) args_str = '' From 5307ac309e01dae05798c3f9de90dc3406de2287 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 14 Sep 2021 21:55:11 -0400 Subject: [PATCH 194/285] JSON: fix nested conditions --- psyneulink/core/globals/json.py | 7 +++++++ tests/json/model_basic.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 751f7370010..36641d100fc 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -602,6 +602,13 @@ def _parse_condition_arg_value(value): if identifier in component_identifiers: return str(identifier) + try: + getattr(psyneulink.core.scheduling.condition, value['type']) + except (AttributeError, KeyError, TypeError): + pass + else: + return _generate_condition_string(value, component_identifiers) + return str(_parse_parameter_value(value, component_identifiers)) args_str = '' diff --git a/tests/json/model_basic.py b/tests/json/model_basic.py index ce970179dd4..92ce6a7679b 100644 --- a/tests/json/model_basic.py +++ b/tests/json/model_basic.py @@ -16,5 +16,5 @@ comp.termination_processing = { pnl.TimeScale.RUN: pnl.AfterNTrials(1), - pnl.TimeScale.TRIAL: pnl.AfterNCalls(B, 4) + pnl.TimeScale.TRIAL: pnl.All(pnl.Not(pnl.BeforeNCalls(B, 5))) } From f95569016c0cfc6f3e1764f829398e318379689e Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 6 May 2021 19:58:55 -0400 Subject: [PATCH 195/285] JSON: update to dict format of MDF replacing list --- psyneulink/core/components/component.py | 4 ++- .../core/components/mechanisms/mechanism.py | 2 +- psyneulink/core/compositions/composition.py | 18 +++++++------ psyneulink/core/globals/json.py | 25 +++++++++++++------ 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 267387fd0dc..aac59ed5a9b 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -3780,7 +3780,9 @@ def parse_parameter_value(value): function_dict = {} try: if isinstance(self.function, Component): - function_dict['functions'] = [self.function._dict_summary] + function_dict['functions'] = { + self.function.name: self.function._dict_summary + } except AttributeError: pass diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 745fb34f0da..1103c7b9272 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -4104,7 +4104,7 @@ def _dict_summary(self): ] } inputs_dict[MODEL_SPEC_ID_INPUT_PORTS].extend( - [s._dict_summary for s in self.parameter_ports] + {s.name: s._dict_summary for s in self.parameter_ports} ) outputs_dict = { diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index dddd1d17624..601f00ddc7c 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -11477,15 +11477,17 @@ def _dict_summary(self): del projections_dict[MODEL_SPEC_ID_PSYNEULINK] return { - MODEL_SPEC_ID_COMPOSITION: [{ - **super_summary, - **self.scheduler._dict_summary, - **{ - MODEL_SPEC_ID_NODES: nodes_dict, - MODEL_SPEC_ID_PROJECTIONS: projections_dict, - 'controller': self.controller, + MODEL_SPEC_ID_COMPOSITION: { + self.name: { + **super_summary, + **self.scheduler._dict_summary, + **{ + MODEL_SPEC_ID_NODES: nodes_dict, + MODEL_SPEC_ID_PROJECTIONS: projections_dict, + 'controller': self.controller, + } } - }] + } } # endregion LLVM diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 36641d100fc..e97ad38bf66 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -455,7 +455,7 @@ def _generate_component_string( # pnl objects only have one function unless specified in another way # than just "function" try: - parameters['function'] = component_dict['functions'][0] + parameters['function'] = component_dict['functions'][list(component_dict['functions'])[0]] except KeyError: pass @@ -643,7 +643,7 @@ def _parse_condition_arg_value(value): return f'psyneulink.{condition_dict[MODEL_SPEC_ID_TYPE]}({arguments_str})' -def _generate_composition_string(composition_list, component_identifiers): +def _generate_composition_string(graphs_dict, component_identifiers): # used if no generic types are specified default_composition_type = psyneulink.Composition default_node_type = psyneulink.ProcessingMechanism @@ -661,13 +661,17 @@ def _generate_composition_string(composition_list, component_identifiers): output = [] # may be given multiple compositions - for composition_dict in composition_list: + for comp_name, composition_dict in graphs_dict.items(): try: comp_type = _parse_component_type(composition_dict) except KeyError: comp_type = default_composition_type - comp_name = composition_dict['name'] + try: + assert comp_name == composition_dict['name'] + except KeyError: + pass + comp_identifer = parse_valid_identifier(comp_name) # get order in which nodes were added @@ -952,11 +956,16 @@ def generate_script_from_json(model_input): """ - def get_declared_identifiers(composition_list): + def get_declared_identifiers(graphs_dict): names = set() - for composition_dict in composition_list: - names.add(parse_valid_identifier(composition_dict['name'])) + for comp_name, composition_dict in graphs_dict.items(): + try: + assert comp_name == composition_dict['name'] + except KeyError: + pass + + names.add(parse_valid_identifier(comp_name)) for name, node in composition_dict[MODEL_SPEC_ID_NODES].items(): if MODEL_SPEC_ID_COMPOSITION in node: names.update( @@ -1075,7 +1084,7 @@ def write_json_file(compositions, filename:str, path:str=None): merged_dict_summary = {} for c in compositions: try: - merged_dict_summary[MODEL_SPEC_ID_COMPOSITION].extend( + merged_dict_summary[MODEL_SPEC_ID_COMPOSITION].update( c._dict_summary[MODEL_SPEC_ID_COMPOSITION] ) except KeyError: From ed0b43964d5537df828d5723f572b89695f3a5a6 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 11 May 2021 19:12:24 -0400 Subject: [PATCH 196/285] JSON: add generate_json method returns JSON/MDF format of a model containing the Compositions passed in --- psyneulink/core/globals/json.py | 58 ++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index e97ad38bf66..8d0acb0de07 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -203,7 +203,7 @@ __all__ = [ 'PNLJSONError', 'JSONDumpable', 'PNLJSONEncoder', - 'generate_script_from_json', + 'generate_json', 'generate_script_from_json', 'write_json_file' ] @@ -1045,6 +1045,43 @@ def get_declared_identifiers(graphs_dict): return model_output +def generate_json(*compositions): + """ + Generate the `general JSON format ` + for one or more `Compositions ` and associated + objects. + .. _JSON_Write_Multiple_Compositions_Note: + + .. note:: + At present, if more than one Composition is specified, all + must be fully disjoint; that is, they must not share any + `Components ` (e.g., `Mechanism`, `Projections` + etc.). This limitation will be addressed in a future update. + + Arguments: + *compositions : Composition + specifies `Composition` or iterable of ones to be output + in JSON + """ + from psyneulink.core.compositions.composition import Composition + + merged_dict_summary = {} + for c in compositions: + if not isinstance(c, Composition): + raise PNLJSONError( + f'Item in compositions arg of {__name__}() is not a Composition: {c}.' + ) + + try: + merged_dict_summary[MODEL_SPEC_ID_COMPOSITION].update( + c._dict_summary[MODEL_SPEC_ID_COMPOSITION] + ) + except KeyError: + merged_dict_summary.update(c._dict_summary) + + return _dump_pnl_json_from_dict(merged_dict_summary) + + def write_json_file(compositions, filename:str, path:str=None): """ Write one or more `Compositions ` and associated objects to file in the `general JSON format @@ -1072,23 +1109,6 @@ def write_json_file(compositions, filename:str, path:str=None): """ compositions = convert_to_list(compositions) - for c in compositions: - from psyneulink.core.compositions.composition import Composition - if not isinstance(c, Composition): - raise PNLJSONError(f'Item in compositions arg of write_to_json_file() is not a Composition: {c}.') - if path: - if path[-1] != '/': - path += '/' - filename = path + filename - - merged_dict_summary = {} - for c in compositions: - try: - merged_dict_summary[MODEL_SPEC_ID_COMPOSITION].update( - c._dict_summary[MODEL_SPEC_ID_COMPOSITION] - ) - except KeyError: - merged_dict_summary.update(c._dict_summary) with open(filename, 'w') as json_file: - json_file.write(_dump_pnl_json_from_dict(merged_dict_summary)) + json_file.write(generate_json(*compositions)) From ed4ecf2cf4d8a3294b2835e9f0e9ec00139c3247 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 11 May 2021 21:08:26 -0400 Subject: [PATCH 197/285] JSON: handle MDF model metadata --- psyneulink/core/globals/json.py | 21 ++++++++++++++++----- psyneulink/core/globals/keywords.py | 7 ++++++- tests/json/test_json.py | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 8d0acb0de07..e296c1a6e79 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -196,7 +196,7 @@ from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, \ - MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE + MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_VERSION from psyneulink.core.globals.sampleiterator import SampleIterator from psyneulink.core.globals.utilities import convert_to_list, get_all_explicit_arguments, \ parse_string_to_psyneulink_object_string, parse_valid_identifier, safe_equals @@ -985,6 +985,9 @@ def get_declared_identifiers(graphs_dict): pass model_input = json.loads(model_input) + assert len(model_input.keys()) == 1 + model_input = model_input[list(model_input.keys())[0]] + imports_str = '' if MODEL_SPEC_ID_COMPOSITION in model_input: # maps declared names to whether they are accessible in the script @@ -1065,7 +1068,9 @@ def generate_json(*compositions): """ from psyneulink.core.compositions.composition import Composition - merged_dict_summary = {} + model_name = "_".join([c.name for c in compositions]) + + merged_graphs_dict_summary = {} for c in compositions: if not isinstance(c, Composition): raise PNLJSONError( @@ -1073,13 +1078,19 @@ def generate_json(*compositions): ) try: - merged_dict_summary[MODEL_SPEC_ID_COMPOSITION].update( + merged_graphs_dict_summary[MODEL_SPEC_ID_COMPOSITION].update( c._dict_summary[MODEL_SPEC_ID_COMPOSITION] ) except KeyError: - merged_dict_summary.update(c._dict_summary) + merged_graphs_dict_summary.update(c._dict_summary) - return _dump_pnl_json_from_dict(merged_dict_summary) + return _dump_pnl_json_from_dict({ + model_name: { + MODEL_SPEC_ID_FORMAT: MODEL_SPEC_ID_VERSION, + MODEL_SPEC_ID_GENERATING_APP: f'psyneulink v{psyneulink.__version__}', + **merged_graphs_dict_summary + } + }) def write_json_file(compositions, filename:str, path:str=None): diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index 33552e6d207..ad355c078d9 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -81,7 +81,8 @@ 'MODULATORY_SIGNALS', 'MONITOR', 'MONITOR_FOR_CONTROL', 'MONITOR_FOR_LEARNING', 'MONITOR_FOR_MODULATION', 'MODEL_SPEC_ID_GENERIC', 'MODEL_SPEC_ID_INPUT_PORTS', 'MODEL_SPEC_ID_OUTPUT_PORTS', 'MODEL_SPEC_ID_PSYNEULINK', 'MODEL_SPEC_ID_SENDER_MECH', 'MODEL_SPEC_ID_SENDER_PORT', - 'MODEL_SPEC_ID_RECEIVER_MECH', 'MODEL_SPEC_ID_RECEIVER_PORT','MODEL_SPEC_ID_PARAMETER_SOURCE', + 'MODEL_SPEC_ID_RECEIVER_MECH', 'MODEL_SPEC_ID_RECEIVER_PORT', 'MODEL_SPEC_ID_GENERATING_APP', 'MODEL_SPEC_ID_FORMAT', + 'MODEL_SPEC_ID_VERSION', 'MODEL_SPEC_ID_PARAMETER_SOURCE', 'MODEL_SPEC_ID_PARAMETER_VALUE', 'MODEL_SPEC_ID_TYPE', 'MSE', 'MULTIPLICATIVE', 'MULTIPLICATIVE_PARAM', 'MUTUAL_ENTROPY', 'NAME', 'NESTED', 'NEWEST', 'NODE', 'NOISE', 'NORMAL_DIST_FUNCTION', 'NORMED_L0_SIMILARITY', 'NOT_EQUAL', @@ -979,6 +980,10 @@ def _is_metric(metric): #endregion # model spec keywords +MODEL_SPEC_ID_GENERATING_APP = 'generating_application' +MODEL_SPEC_ID_FORMAT = 'format' +MODEL_SPEC_ID_VERSION = 'ModECI MDF v0.1' + MODEL_SPEC_ID_TYPE = 'type' MODEL_SPEC_ID_PSYNEULINK = 'PNL' MODEL_SPEC_ID_GENERIC = 'generic' diff --git a/tests/json/test_json.py b/tests/json/test_json.py index c7a159c1af6..3276ec05696 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -54,7 +54,7 @@ def test_json_results_equivalence( pnl.core.globals.utilities.set_global_seed(0) # Generate python script from JSON summary of composition and execute - json_summary = eval(f'{composition_name}.json_summary') + json_summary = pnl.generate_json(eval(f'{composition_name}')) exec(pnl.generate_script_from_json(json_summary)) exec(f'{composition_name}.run(inputs={input_dict_str})') new_results = eval(f'{composition_name}.results') From 384ca2dae66edfb4b9487b2cfdd744dc1e69c635 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 11 May 2021 22:04:47 -0400 Subject: [PATCH 198/285] JSON: handle optional names, conditions --- psyneulink/core/globals/json.py | 108 ++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index e296c1a6e79..dfa8d8d3c97 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -423,6 +423,7 @@ def _parse_parameter_value(value, component_identifiers=None): def _generate_component_string( component_dict, component_identifiers, + component_name=None, assignment=False, default_type=None # used if no PNL or generic types are specified ): @@ -438,8 +439,18 @@ def _generate_component_string( 'default_type is specified' ) from e - name = component_dict['name'] - parameters = dict(component_dict[component_type._model_spec_id_parameters]) + if component_name is None: + name = component_dict['name'] + else: + name = component_name + try: + assert component_name == component_dict['name'] + except KeyError: + pass + try: + parameters = dict(component_dict[component_type._model_spec_id_parameters]) + except KeyError: + parameters = {} # If there is a parameter that is the psyneulink identifier string # (as of this comment, 'pnl'), then expand these parameters as @@ -557,37 +568,47 @@ def _generate_scheduler_string( blacklist=[] ): output = [] - for node, condition in scheduler_dict['node_specific'].items(): - if node not in blacklist: - output.append( - '{0}.add_condition({1}, {2})'.format( - scheduler_id, - parse_valid_identifier(node), - _generate_condition_string( - condition, - component_identifiers + try: + node_specific_conds = scheduler_dict['node_specific'] + except KeyError: + pass + else: + for node, condition in node_specific_conds.items(): + if node not in blacklist: + output.append( + '{0}.add_condition({1}, {2})'.format( + scheduler_id, + parse_valid_identifier(node), + _generate_condition_string( + condition, + component_identifiers + ) ) ) - ) - output.append('') + output.append('') termination_str = [] - for scale, cond in scheduler_dict['termination'].items(): - termination_str.insert( - 1, - 'psyneulink.{0}: {1}'.format( - f'TimeScale.{str.upper(scale)}', - _generate_condition_string(cond, component_identifiers) + try: + termination_conds = scheduler_dict['termination'] + except KeyError: + pass + else: + for scale, cond in termination_conds.items(): + termination_str.insert( + 1, + 'psyneulink.{0}: {1}'.format( + f'TimeScale.{str.upper(scale)}', + _generate_condition_string(cond, component_identifiers) + ) ) - ) - output.append( - '{0}.termination_conds = {{{1}}}'.format( - scheduler_id, - ', '.join(termination_str) + output.append( + '{0}.termination_conds = {{{1}}}'.format( + scheduler_id, + ', '.join(termination_str) + ) ) - ) return '\n'.join(output) @@ -758,16 +779,17 @@ def _generate_composition_string(graphs_dict, component_identifiers): _generate_component_string( composition_dict, component_identifiers, + component_name=comp_name, default_type=default_composition_type ) ) ) component_identifiers[comp_identifer] = True - mechanisms = [] - compositions = [] - control_mechanisms = [] - implicit_mechanisms = [] + mechanisms = {} + compositions = {} + control_mechanisms = {} + implicit_mechanisms = {} # add nested compositions and mechanisms in order they were added # to this composition @@ -776,7 +798,7 @@ def _generate_composition_string(graphs_dict, component_identifiers): key=lambda item: node_order[parse_valid_identifier(item[0])] ): if MODEL_SPEC_ID_COMPOSITION in node: - compositions.append(node[MODEL_SPEC_ID_COMPOSITION]) + compositions[name] = node[MODEL_SPEC_ID_COMPOSITION] else: try: component_type = _parse_component_type(node) @@ -784,24 +806,25 @@ def _generate_composition_string(graphs_dict, component_identifiers): component_type = default_node_type identifier = parse_valid_identifier(name) if issubclass(component_type, control_mechanism_types): - control_mechanisms.append(node) + control_mechanisms[name] = node component_identifiers[identifier] = True elif issubclass(component_type, implicit_types): - implicit_mechanisms.append(node) + implicit_mechanisms[name] = node else: - mechanisms.append(node) + mechanisms[name] = node component_identifiers[identifier] = True implicit_names = [ - x['name'] - for x in implicit_mechanisms + control_mechanisms + x + for x in [*implicit_mechanisms.keys(), *control_mechanisms.keys()] ] - for mech in mechanisms: + for name, mech in mechanisms.items(): output.append( _generate_component_string( mech, component_identifiers, + component_name=name, assignment=True, default_type=default_node_type ) @@ -809,11 +832,12 @@ def _generate_composition_string(graphs_dict, component_identifiers): if len(mechanisms) > 0: output.append('') - for mech in control_mechanisms: + for name, mech in control_mechanisms.items(): output.append( _generate_component_string( mech, component_identifiers, + component_name=name, assignment=True, default_type=default_node_type ) @@ -823,7 +847,7 @@ def _generate_composition_string(graphs_dict, component_identifiers): output.append('') # recursively generate string for inner Compositions - for comp in compositions: + for name, comp in compositions.items(): output.append( _generate_composition_string( comp, @@ -893,6 +917,7 @@ def _generate_composition_string(graphs_dict, component_identifiers): _generate_component_string( projection_dict, component_identifiers, + component_name=name, default_type=default_edge_type ), parse_valid_identifier( @@ -916,11 +941,16 @@ def _generate_composition_string(graphs_dict, component_identifiers): # add schedulers # blacklist automatically generated nodes because they will # not exist in the script namespace + try: + conditions = composition_dict['conditions'] + except KeyError: + conditions = {} + output.append('') output.append( _generate_scheduler_string( f'{comp_identifer}.scheduler', - composition_dict['conditions'], + conditions, component_identifiers, blacklist=implicit_names ) From 790c6b198bcf02481ad7b036d62bd7e1736b55f9 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 11 Jun 2021 00:57:35 -0400 Subject: [PATCH 199/285] JSON: improve detection of external modules --- psyneulink/core/globals/json.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index dfa8d8d3c97..7335969c312 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -1048,7 +1048,12 @@ def get_declared_identifiers(graphs_dict): } module_names = set() - potential_module_names = set(re.findall(r'([A-Za-z]+?)\.', comp_str)) + + # greedy and non-greedy + potential_module_names = set([ + *re.findall(r'([A-Za-z_\.]+)\.', comp_str), + *re.findall(r'([A-Za-z_\.]+?)\.', comp_str) + ]) for module in potential_module_names: try: exec(f'import {module}') @@ -1056,18 +1061,32 @@ def get_declared_identifiers(graphs_dict): except (ImportError, ModuleNotFoundError, SyntaxError): pass - for module in module_names: + for module in module_names.copy(): try: friendly_name = module_friendly_name_mapping[module] comp_str = re.sub(f'{module}\\.', f'{friendly_name}.', comp_str) except KeyError: friendly_name = module - if f'{friendly_name}.' in comp_str: - imports_str += 'import {0}{1}\n'.format( - module, - f' as {friendly_name}' if friendly_name != module else '' - ) + if not re.findall(rf'[^\.]{friendly_name}\.', comp_str): + module_names.remove(module) + + for m in module_names.copy(): + for n in module_names.copy(): + # remove potential modules that are substrings of another + if m is not n and m in n: + module_names.remove(m) + + for module in sorted(module_names): + try: + friendly_name = module_friendly_name_mapping[module] + except KeyError: + friendly_name = module + + imports_str += 'import {0}{1}\n'.format( + module, + f' as {friendly_name}' if friendly_name != module else '' + ) model_output = '{0}{1}{2}'.format( imports_str, From 7c75c2087431b6d3bb1f2a3155ce5fe4dfa4363a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 11 May 2021 23:39:29 -0400 Subject: [PATCH 200/285] JSON: support python math functions as UserDefinedFunction --- psyneulink/core/globals/json.py | 59 +++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 7335969c312..a5f236896a6 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -187,6 +187,7 @@ import dill import enum import json +import math import numpy import pickle import psyneulink @@ -282,6 +283,20 @@ def _dump_pnl_json_from_dict(dict_summary): def _parse_component_type(component_dict): type_dict = component_dict[MODEL_SPEC_ID_TYPE] + def get_pnl_component_type(s): + from psyneulink.core.components.component import ComponentsMeta + + try: + return getattr(psyneulink, s) + except AttributeError: + for o in dir(psyneulink): + if s.lower() == o.lower(): + o = getattr(psyneulink, o) + if isinstance(o, ComponentsMeta): + return o + # if matching component not found, raise original exception + raise + try: type_str = type_dict[MODEL_SPEC_ID_PSYNEULINK] except KeyError: @@ -290,13 +305,22 @@ def _parse_component_type(component_dict): try: # gets the actual psyneulink type (Component, etc..) from the module - return getattr(psyneulink, type_str) - except AttributeError as e: - raise PNLJSONError( - 'Invalid PsyNeuLink type specified for JSON object: {0}'.format( - component_dict - ) - ) from e + return get_pnl_component_type(type_str) + except (AttributeError, TypeError): + pass + + try: + getattr(math, type_str) + except (AttributeError, TypeError): + pass + else: + return f'math.{type_str}' + + raise PNLJSONError( + 'Invalid type specified for JSON object: {0}'.format( + component_dict + ) + ) def _parse_parameter_value(value, component_identifiers=None): @@ -427,6 +451,8 @@ def _generate_component_string( assignment=False, default_type=None # used if no PNL or generic types are specified ): + from psyneulink.core.components.functions.userdefinedfunction import UserDefinedFunction + try: component_type = _parse_component_type(component_dict) except KeyError as e: @@ -447,8 +473,21 @@ def _generate_component_string( assert component_name == component_dict['name'] except KeyError: pass + try: parameters = dict(component_dict[component_type._model_spec_id_parameters]) + except AttributeError: + custom_func = component_type + component_type = UserDefinedFunction + try: + parameters = dict(component_dict[component_type._model_spec_id_parameters]) + except KeyError: + pass + parameters['custom_function'] = f'{custom_func}' + try: + del parameters[MODEL_SPEC_ID_PSYNEULINK]['custom_function'] + except KeyError: + pass except KeyError: parameters = {} @@ -549,6 +588,12 @@ def _generate_component_string( or dill.dumps(eval(val)) != dill.dumps(default_val) ): additional_arguments.append(f'{constructor_arg}={val}') + elif component_type is UserDefinedFunction: + val = _parse_parameter_value( + val, component_identifiers, + ) + + additional_arguments.append(f'{constructor_arg}={val}') output = '{0}psyneulink.{1}{2}{3}{4}'.format( assignment_str, From 658cf33a17782c7af961edb5d061d1ded8358dbf Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 11 Jun 2021 00:56:20 -0400 Subject: [PATCH 201/285] JSON: support MDF library functions as UserDefinedFunction --- psyneulink/core/globals/json.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index a5f236896a6..a04aa16597c 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -309,6 +309,15 @@ def get_pnl_component_type(s): except (AttributeError, TypeError): pass + try: + from modeci_mdf.functions.standard import mdf_functions + mdf_functions[type_str]['function'] + # remove import/module errors when modeci_mdf is a package + except (ImportError, KeyError, ModuleNotFoundError): + pass + else: + return f"modeci_mdf.functions.standard.mdf_functions['{type_str}']['function']" + try: getattr(math, type_str) except (AttributeError, TypeError): From c35fb58ba5b53822bd5927ad941b5b44420ecce7 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 11 May 2021 23:44:18 -0400 Subject: [PATCH 202/285] JSON: handle name as optional field of parameter functions --- psyneulink/core/globals/json.py | 54 +++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index a04aa16597c..4039e25d670 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -332,14 +332,14 @@ def get_pnl_component_type(s): ) -def _parse_parameter_value(value, component_identifiers=None): +def _parse_parameter_value(value, component_identifiers=None, name=None): if component_identifiers is None: component_identifiers = {} exec('import numpy') if isinstance(value, list): - value = [_parse_parameter_value(x, component_identifiers) for x in value] + value = [_parse_parameter_value(x, component_identifiers, name) for x in value] value = f"[{', '.join([str(x) for x in value])}]" elif isinstance(value, dict): if ( @@ -358,7 +358,8 @@ def _parse_parameter_value(value, component_identifiers=None): value = _parse_parameter_value( value[MODEL_SPEC_ID_PARAMETER_VALUE], - component_identifiers + component_identifiers, + name, ) # handle tuples and numpy arrays, which both are dumped @@ -374,7 +375,29 @@ def _parse_parameter_value(value, component_identifiers=None): # it is either a Component spec or just a plain dict try: # try handling as a Component spec - identifier = parse_valid_identifier(value['name']) + try: + comp_name = value['name'] + except KeyError: + comp_name = name + + if comp_name is not None: + identifier = parse_valid_identifier(comp_name) + if len(value) == 1: + try: + value = value[comp_name] + except KeyError: + pass + else: + if len(value) == 1: + comp_name = list(value.keys())[0] + identifier = parse_valid_identifier(comp_name) + if isinstance(value[comp_name], dict): + value = value[comp_name] + else: + raise PNLJSONError( + f'Component without name could reference multiple objects: {value}', + ) + if ( identifier in component_identifiers and component_identifiers[identifier] @@ -385,15 +408,16 @@ def _parse_parameter_value(value, component_identifiers=None): else: value = _generate_component_string( value, - component_identifiers + component_identifiers, + component_name=comp_name, ) - except (PNLJSONError, KeyError): + except (PNLJSONError, KeyError, TypeError): # standard dict handling value = '{{{0}}}'.format( ', '.join([ '{0}: {1}'.format( - str(_parse_parameter_value(k, component_identifiers)), - str(_parse_parameter_value(v, component_identifiers)) + str(_parse_parameter_value(k, component_identifiers, name)), + str(_parse_parameter_value(v, component_identifiers, name)) ) for k, v in value.items() ]) @@ -500,6 +524,8 @@ def _generate_component_string( except KeyError: parameters = {} + parameter_names = {} + # If there is a parameter that is the psyneulink identifier string # (as of this comment, 'pnl'), then expand these parameters as # normal ones. We don't check and expand for other @@ -514,7 +540,8 @@ def _generate_component_string( # pnl objects only have one function unless specified in another way # than just "function" try: - parameters['function'] = component_dict['functions'][list(component_dict['functions'])[0]] + parameter_names['function'] = list(component_dict['functions'])[0] + parameters['function'] = component_dict['functions'][parameter_names['function']] except KeyError: pass @@ -550,7 +577,14 @@ def _generate_component_string( constructor_arg = arg if constructor_arg in constructor_arguments: - val = _parse_parameter_value(val, component_identifiers) + try: + val = _parse_parameter_value( + val, component_identifiers, + name=parameter_names[arg] + ) + except KeyError: + val = _parse_parameter_value(val, component_identifiers) + default_val = getattr(component_type.defaults, arg) evaled_val = NotImplemented From 75d844ede6a36cd87004f85190f97efad5456185 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 11 May 2021 23:39:18 -0400 Subject: [PATCH 203/285] JSON: workaround inconsistent MDF type name (function: 'function') support exporting function type in 'function' key alongside 'type' key --- .../core/components/functions/function.py | 17 ++++++++++++++++- psyneulink/core/globals/json.py | 14 +++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index b4c817b9563..7f291fa3818 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -155,7 +155,8 @@ from psyneulink.core.globals.keywords import ( ARGUMENT_THERAPY_FUNCTION, AUTO_ASSIGN_MATRIX, EXAMPLE_FUNCTION_TYPE, FULL_CONNECTIVITY_MATRIX, FUNCTION_COMPONENT_CATEGORY, FUNCTION_OUTPUT_TYPE, FUNCTION_OUTPUT_TYPE_CONVERSION, HOLLOW_MATRIX, - IDENTITY_MATRIX, INVERSE_HOLLOW_MATRIX, NAME, PREFERENCE_SET_NAME, RANDOM_CONNECTIVITY_MATRIX, VALUE, VARIABLE + IDENTITY_MATRIX, INVERSE_HOLLOW_MATRIX, NAME, PREFERENCE_SET_NAME, RANDOM_CONNECTIVITY_MATRIX, VALUE, VARIABLE, + MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC ) from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import REPORT_OUTPUT_PREF, is_pref_set @@ -814,6 +815,20 @@ def _model_spec_parameter_blacklist(self): 'multiplicative_param', 'additive_param', }) + @property + def _dict_summary(self): + summary = super()._dict_summary + + try: + type_str = summary[MODEL_SPEC_ID_TYPE][MODEL_SPEC_ID_PSYNEULINK] + except KeyError: + type_str = summary[MODEL_SPEC_ID_TYPE][MODEL_SPEC_ID_GENERIC] + + return { + **summary, + 'function': type_str.lower() + } + # ***************************************** EXAMPLE FUNCTION ******************************************************* PROPENSITY = "PROPENSITY" diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 4039e25d670..381cfaa80d7 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -281,8 +281,6 @@ def _dump_pnl_json_from_dict(dict_summary): def _parse_component_type(component_dict): - type_dict = component_dict[MODEL_SPEC_ID_TYPE] - def get_pnl_component_type(s): from psyneulink.core.components.component import ComponentsMeta @@ -298,10 +296,16 @@ def get_pnl_component_type(s): raise try: - type_str = type_dict[MODEL_SPEC_ID_PSYNEULINK] + type_dict = component_dict[MODEL_SPEC_ID_TYPE] except KeyError: - # catch error outside of this function if necessary - type_str = type_dict[MODEL_SPEC_ID_GENERIC] + # specifically for functions the keyword is not 'type' + type_str = component_dict['function'] + else: + try: + type_str = type_dict[MODEL_SPEC_ID_PSYNEULINK] + except KeyError: + # catch error outside of this function if necessary + type_str = type_dict[MODEL_SPEC_ID_GENERIC] try: # gets the actual psyneulink type (Component, etc..) from the module From df7188542c0b028dcd791d3edab4658c7390df96 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 12 May 2021 23:06:49 -0400 Subject: [PATCH 204/285] JSON: fix unintentional module import for component identifier --- psyneulink/core/globals/json.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 381cfaa80d7..ad7805f9cee 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -1147,11 +1147,12 @@ def get_declared_identifiers(graphs_dict): *re.findall(r'([A-Za-z_\.]+?)\.', comp_str) ]) for module in potential_module_names: - try: - exec(f'import {module}') - module_names.add(module) - except (ImportError, ModuleNotFoundError, SyntaxError): - pass + if module not in component_identifiers: + try: + exec(f'import {module}') + module_names.add(module) + except (ImportError, ModuleNotFoundError, SyntaxError): + pass for module in module_names.copy(): try: From 47ef7a2dfe027bd4446ec88acb16adc3b810e10a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 13 May 2021 00:14:47 -0400 Subject: [PATCH 205/285] JSON: workaround for function specified as node --- psyneulink/core/globals/json.py | 52 ++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index ad7805f9cee..eadc5681451 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -184,6 +184,7 @@ import abc import base64 import binascii +import copy import dill import enum import json @@ -757,6 +758,30 @@ def _parse_condition_arg_value(value): def _generate_composition_string(graphs_dict, component_identifiers): + def _replace_function_node_with_mech_node(function_dict, name, typ=None): + if typ is None: + typ = _parse_component_type(function_dict) + else: + typ = typ.__name__ + + mech_func_dict = { + 'functions': { + name: { + MODEL_SPEC_ID_TYPE: {MODEL_SPEC_ID_PSYNEULINK: typ}, + psyneulink.Function_Base._model_spec_id_parameters: function_dict[psyneulink.Component._model_spec_id_parameters] + }, + } + } + + try: + del function_dict[MODEL_SPEC_ID_TYPE] + except KeyError: + pass + + function_dict['name'] = f"{name}_wrapped_mech" + + return {**function_dict, **mech_func_dict} + # used if no generic types are specified default_composition_type = psyneulink.Composition default_node_type = psyneulink.ProcessingMechanism @@ -911,7 +936,32 @@ def _generate_composition_string(graphs_dict, component_identifiers): for x in [*implicit_mechanisms.keys(), *control_mechanisms.keys()] ] - for name, mech in mechanisms.items(): + for name, mech in copy.copy(mechanisms).items(): + try: + mech_type = _parse_component_type(mech) + except KeyError: + mech_type = None + + if ( + isinstance(mech_type, type) + and issubclass(mech_type, psyneulink.Function) + ): + mech = _replace_function_node_with_mech_node(mech, name, mech_type) + + component_identifiers[mech['name']] = component_identifiers[name] + del component_identifiers[name] + + node_order[mech['name']] = node_order[name] + del node_order[name] + + mechanisms[mech['name']] = mechanisms[name] + del mechanisms[name] + + composition_dict['nodes'][mech['name']] = composition_dict['nodes'][name] + del composition_dict['nodes'][name] + + name = mech['name'] + output.append( _generate_component_string( mech, From f08a13574cd8092a8bf0d32928aea8c304f68383 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 13 May 2021 22:45:46 -0400 Subject: [PATCH 206/285] JSON: support referencing parameters of a node in its function --- psyneulink/core/globals/json.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index eadc5681451..4217196e3ee 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -337,14 +337,14 @@ def get_pnl_component_type(s): ) -def _parse_parameter_value(value, component_identifiers=None, name=None): +def _parse_parameter_value(value, component_identifiers=None, name=None, parent_parameters=None): if component_identifiers is None: component_identifiers = {} exec('import numpy') if isinstance(value, list): - value = [_parse_parameter_value(x, component_identifiers, name) for x in value] + value = [_parse_parameter_value(x, component_identifiers, name, parent_parameters) for x in value] value = f"[{', '.join([str(x) for x in value])}]" elif isinstance(value, dict): if ( @@ -365,6 +365,7 @@ def _parse_parameter_value(value, component_identifiers=None, name=None): value[MODEL_SPEC_ID_PARAMETER_VALUE], component_identifiers, name, + parent_parameters, ) # handle tuples and numpy arrays, which both are dumped @@ -415,6 +416,7 @@ def _parse_parameter_value(value, component_identifiers=None, name=None): value, component_identifiers, component_name=comp_name, + parent_parameters=parent_parameters ) except (PNLJSONError, KeyError, TypeError): # standard dict handling @@ -429,6 +431,13 @@ def _parse_parameter_value(value, component_identifiers=None, name=None): ) elif isinstance(value, str): + # handle pointer to parent's parameter value + try: + return _parse_parameter_value(parent_parameters[value]) + except (KeyError, TypeError): + pass + + # handle reference to psyneulink object obj_string = parse_string_to_psyneulink_object_string(value) if obj_string is not None: return f'psyneulink.{obj_string}' @@ -486,6 +495,7 @@ def _generate_component_string( component_dict, component_identifiers, component_name=None, + parent_parameters=None, assignment=False, default_type=None # used if no PNL or generic types are specified ): @@ -564,6 +574,9 @@ def _generate_component_string( if 'name' in constructor_arguments: additional_arguments.append(f"name='{name}'") + if parent_parameters is None: + parent_parameters = parameters + # sort on arg name for arg, val in sorted(parameters.items(), key=lambda p: p[0]): try: @@ -585,10 +598,11 @@ def _generate_component_string( try: val = _parse_parameter_value( val, component_identifiers, - name=parameter_names[arg] + name=parameter_names[arg], + parent_parameters=parent_parameters, ) except KeyError: - val = _parse_parameter_value(val, component_identifiers) + val = _parse_parameter_value(val, component_identifiers, parent_parameters=parent_parameters) default_val = getattr(component_type.defaults, arg) @@ -638,7 +652,7 @@ def _generate_component_string( additional_arguments.append(f'{constructor_arg}={val}') elif component_type is UserDefinedFunction: val = _parse_parameter_value( - val, component_identifiers, + val, component_identifiers, parent_parameters=parent_parameters ) additional_arguments.append(f'{constructor_arg}={val}') From ddab10ac736ec8203e719c0e22d251e4ed06e0a9 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 13 May 2021 23:42:41 -0400 Subject: [PATCH 207/285] JSON: remove UDF variable0 argument (assume corresponds to input) --- psyneulink/core/globals/json.py | 11 ++++++----- psyneulink/core/globals/keywords.py | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 4217196e3ee..caee271df31 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -198,7 +198,7 @@ from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, \ - MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_VERSION + MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_VERSION, MODEL_SPEC_ID_MDF_VARIABLE from psyneulink.core.globals.sampleiterator import SampleIterator from psyneulink.core.globals.utilities import convert_to_list, get_all_explicit_arguments, \ parse_string_to_psyneulink_object_string, parse_valid_identifier, safe_equals @@ -651,11 +651,12 @@ def _generate_component_string( ): additional_arguments.append(f'{constructor_arg}={val}') elif component_type is UserDefinedFunction: - val = _parse_parameter_value( - val, component_identifiers, parent_parameters=parent_parameters - ) + if arg != MODEL_SPEC_ID_MDF_VARIABLE: + val = _parse_parameter_value( + val, component_identifiers, parent_parameters=parent_parameters + ) - additional_arguments.append(f'{constructor_arg}={val}') + additional_arguments.append(f'{constructor_arg}={val}') output = '{0}psyneulink.{1}{2}{3}{4}'.format( assignment_str, diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index ad355c078d9..f528cc3d0fe 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -1002,3 +1002,5 @@ def _is_metric(metric): MODEL_SPEC_ID_NODES = 'nodes' MODEL_SPEC_ID_PROJECTIONS = 'edges' MODEL_SPEC_ID_COMPOSITION = 'graphs' + +MODEL_SPEC_ID_MDF_VARIABLE = 'variable0' From 322752f8a232fbdfa7bc19ec3c242c37afcf0bdf Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 14 May 2021 22:42:16 -0400 Subject: [PATCH 208/285] JSON: handle input_port shape as fallback for node variable --- psyneulink/core/globals/json.py | 21 +++++++++++++++++++-- psyneulink/core/globals/keywords.py | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index caee271df31..e6a1286e4ad 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -182,6 +182,7 @@ """ import abc +import ast import base64 import binascii import copy @@ -198,10 +199,10 @@ from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, \ - MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_VERSION, MODEL_SPEC_ID_MDF_VARIABLE + MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_VERSION, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE from psyneulink.core.globals.sampleiterator import SampleIterator from psyneulink.core.globals.utilities import convert_to_list, get_all_explicit_arguments, \ - parse_string_to_psyneulink_object_string, parse_valid_identifier, safe_equals + parse_string_to_psyneulink_object_string, parse_valid_identifier, safe_equals, convert_to_np_array __all__ = [ 'PNLJSONError', 'JSONDumpable', 'PNLJSONEncoder', @@ -499,6 +500,7 @@ def _generate_component_string( assignment=False, default_type=None # used if no PNL or generic types are specified ): + from psyneulink.core.components.functions.function import Function_Base from psyneulink.core.components.functions.userdefinedfunction import UserDefinedFunction try: @@ -577,6 +579,21 @@ def _generate_component_string( if parent_parameters is None: parent_parameters = parameters + if 'variable' not in parameters: + try: + ip = parameters['function'][Function_Base._model_spec_id_parameters][MODEL_SPEC_ID_MDF_VARIABLE] + var = convert_to_np_array( + numpy.zeros( + ast.literal_eval( + component_dict[MODEL_SPEC_ID_INPUT_PORTS][ip][MODEL_SPEC_ID_SHAPE] + ) + ), + dimension=2 + ).tolist() + parameters['variable'] = var + except KeyError: + pass + # sort on arg name for arg, val in sorted(parameters.items(), key=lambda p: p[0]): try: diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index f528cc3d0fe..a4768f1abe4 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -1004,3 +1004,5 @@ def _is_metric(metric): MODEL_SPEC_ID_COMPOSITION = 'graphs' MODEL_SPEC_ID_MDF_VARIABLE = 'variable0' + +MODEL_SPEC_ID_SHAPE = 'shape' From 86aa7577a1855a4fb90053173306c008b2854cbc Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 11 Jun 2021 19:18:32 -0400 Subject: [PATCH 209/285] JSON: split parameters into regular and stateful --- psyneulink/core/components/component.py | 4 +++- psyneulink/core/globals/json.py | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index aac59ed5a9b..ca573cb5595 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -910,6 +910,7 @@ class Component(JSONDumpable, metaclass=ComponentsMeta): # helper attributes for JSON model spec _model_spec_id_parameters = 'parameters' + _model_spec_id_stateful_parameters = 'stateful_parameters' _model_spec_generic_type_name = NotImplemented """ @@ -3800,7 +3801,8 @@ def parse_parameter_value(value): return { **{attr: getattr(self, attr) for attr in basic_attributes}, - **{self._model_spec_id_parameters: parameters_dict}, + **{self._model_spec_id_parameters: {k: v for k, v in parameters_dict.items() if 'previous' not in k}}, + **{self._model_spec_id_stateful_parameters: {k: v for k, v in parameters_dict.items() if 'previous' in k}}, **function_dict, **{MODEL_SPEC_ID_TYPE: type_dict} } diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index e6a1286e4ad..4b522594bce 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -541,6 +541,11 @@ def _generate_component_string( except KeyError: parameters = {} + try: + parameters.update(component_dict[component_type._model_spec_id_stateful_parameters]) + except KeyError: + pass + parameter_names = {} # If there is a parameter that is the psyneulink identifier string From e54d6afdb7dfad4aeb4331fab0493dab63ab4835 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 2 Nov 2021 21:54:18 -0400 Subject: [PATCH 210/285] JSON: handle dump of np.number explcitly --- psyneulink/core/globals/json.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 4b522594bce..6406a0f1ec7 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -262,12 +262,8 @@ def default(self, o): return list(o) elif isinstance(o, numpy.random.RandomState): return f'numpy.random.RandomState({o.seed})' - else: - try: - # convert numpy number type to python type - return o.item() - except AttributeError: - pass + elif isinstance(o, numpy.number): + return o.item() return super().default(o) From b03eeb11b0f8a7ef7598c177aa461fab1377e9db Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 2 Nov 2021 21:54:35 -0400 Subject: [PATCH 211/285] JSON: fallback dump to str if no default handler --- psyneulink/core/globals/json.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 6406a0f1ec7..e76e1ced797 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -265,7 +265,10 @@ def default(self, o): elif isinstance(o, numpy.number): return o.item() - return super().default(o) + try: + return super().default(o) + except TypeError: + return str(o) def _dump_pnl_json_from_dict(dict_summary): From 06bf9aa9f4a5b3174f19df83953f5774b07ca534 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 11 Jun 2021 23:18:35 -0400 Subject: [PATCH 212/285] JSON: handle dump/import of pint units/quantities --- psyneulink/core/globals/json.py | 15 +++++++++++++++ tests/json/model_nested_comp_with_scheduler.py | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index e76e1ced797..64a274ee2c8 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -192,6 +192,7 @@ import math import numpy import pickle +import pint import psyneulink import re import types @@ -470,6 +471,20 @@ def _parse_parameter_value(value, component_identifiers=None, name=None, parent_ if identifier in component_identifiers: value = identifier + try: + psyneulink._unit_registry.Unit(value) + except (AttributeError, TypeError, ValueError, pint.errors.DefinitionSyntaxError): + pass + else: + value = f"'{value}'" + + try: + psyneulink._unit_registry.Quantity(value) + except (AttributeError, TypeError, ValueError, pint.errors.DefinitionSyntaxError): + pass + else: + value = f"'{value}'" + evaluates = False try: eval(value) diff --git a/tests/json/model_nested_comp_with_scheduler.py b/tests/json/model_nested_comp_with_scheduler.py index 045e4eed4c1..637c2cd86d0 100644 --- a/tests/json/model_nested_comp_with_scheduler.py +++ b/tests/json/model_nested_comp_with_scheduler.py @@ -29,7 +29,8 @@ comp.scheduler.add_condition_set({ A: pnl.EveryNPasses(1), B: pnl.EveryNCalls(A, 2), - C: pnl.EveryNCalls(B, 2) + C: pnl.EveryNCalls(B, 2), + D: pnl.TimeInterval(start=1), }) comp.termination_processing = { From 3f662b4e7bf11e29444471eb53a7e6faf27e3f7f Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 7 Jul 2021 20:13:54 -0400 Subject: [PATCH 213/285] JSON: generate_script_from_json: add outfile argument to write to file --- psyneulink/core/globals/json.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 64a274ee2c8..2d612e407e4 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -1152,7 +1152,7 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): return '\n'.join(output) -def generate_script_from_json(model_input): +def generate_script_from_json(model_input, outfile=None): """ Generate a Python script from JSON **model_input** in the `general JSON format ` @@ -1206,7 +1206,13 @@ def get_declared_identifiers(graphs_dict): model_input = open(model_input, 'r').read() except (FileNotFoundError, OSError): pass - model_input = json.loads(model_input) + + try: + model_input = json.loads(model_input) + except json.decoder.JSONDecodeError: + raise ValueError( + f'{model_input} is neither valid JSON nor a file containing JSON' + ) assert len(model_input.keys()) == 1 model_input = model_input[list(model_input.keys())[0]] @@ -1288,7 +1294,13 @@ def get_declared_identifiers(graphs_dict): comp_str ) - return model_output + if outfile is not None: + # pass through any file exceptions + with open(outfile, 'w') as outfile: + outfile.write(model_output) + print(f'Wrote JSON to {outfile.name}') + else: + return model_output def generate_json(*compositions): From 67f96b7ac7e729aa0f7881d5f02b478682aa6790 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 7 Jul 2021 20:14:33 -0400 Subject: [PATCH 214/285] JSON: make edges optional --- psyneulink/core/globals/json.py | 108 +++++++++++++++++--------------- 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 2d612e407e4..549f36116ef 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -914,31 +914,36 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): for name_to_delete in keys_to_delete: del composition_dict[MODEL_SPEC_ID_NODES][name_to_delete] - pnl_specific_items = {} - keys_to_delete = [] - for name, edge in composition_dict[MODEL_SPEC_ID_PROJECTIONS].items(): - try: - _parse_component_type(edge) - except KeyError: - # will use a default type - pass - except PNLJSONError: - if name == MODEL_SPEC_ID_PSYNEULINK: - pnl_specific_items = edge + try: + edges_dict = composition_dict[MODEL_SPEC_ID_PROJECTIONS] + pnl_specific_items = {} + keys_to_delete = [] + except KeyError: + pass + else: + for name, edge in edges_dict.items(): + try: + _parse_component_type(edge) + except KeyError: + # will use a default type + pass + except PNLJSONError: + if name == MODEL_SPEC_ID_PSYNEULINK: + pnl_specific_items = edge - keys_to_delete.append(name) + keys_to_delete.append(name) - for name, edge in pnl_specific_items.items(): - # exclude CIM projections because they are automatically - # generated - if ( - edge[MODEL_SPEC_ID_SENDER_MECH] != comp_name - and edge[MODEL_SPEC_ID_RECEIVER_MECH] != comp_name - ): - composition_dict[MODEL_SPEC_ID_PROJECTIONS][name] = edge + for name, edge in pnl_specific_items.items(): + # exclude CIM projections because they are automatically + # generated + if ( + edge[MODEL_SPEC_ID_SENDER_MECH] != comp_name + and edge[MODEL_SPEC_ID_RECEIVER_MECH] != comp_name + ): + composition_dict[MODEL_SPEC_ID_PROJECTIONS][name] = edge - for name_to_delete in keys_to_delete: - del composition_dict[MODEL_SPEC_ID_PROJECTIONS][name_to_delete] + for name_to_delete in keys_to_delete: + del composition_dict[MODEL_SPEC_ID_PROJECTIONS][name_to_delete] # generate string for Composition itself output.append( @@ -1092,35 +1097,40 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): if len(composition_dict[MODEL_SPEC_ID_NODES]) > 0: output.append('') - # generate string to add the projections - for name, projection_dict in composition_dict[MODEL_SPEC_ID_PROJECTIONS].items(): - try: - projection_type = _parse_component_type(projection_dict) - except KeyError: - projection_type = default_edge_type + try: + edges_dict = composition_dict[MODEL_SPEC_ID_PROJECTIONS] + except KeyError: + pass + else: + # generate string to add the projections + for name, projection_dict in edges_dict.items(): + try: + projection_type = _parse_component_type(projection_dict) + except KeyError: + projection_type = default_edge_type - if ( - not issubclass(projection_type, implicit_types) - and projection_dict[MODEL_SPEC_ID_SENDER_MECH] not in implicit_names - and projection_dict[MODEL_SPEC_ID_RECEIVER_MECH] not in implicit_names - ): - output.append( - '{0}.add_projection(projection={1}, sender={2}, receiver={3})'.format( - comp_identifer, - _generate_component_string( - projection_dict, - component_identifiers, - component_name=name, - default_type=default_edge_type - ), - parse_valid_identifier( - projection_dict[MODEL_SPEC_ID_SENDER_MECH] - ), - parse_valid_identifier( - projection_dict[MODEL_SPEC_ID_RECEIVER_MECH] - ), + if ( + not issubclass(projection_type, implicit_types) + and projection_dict[MODEL_SPEC_ID_SENDER_MECH] not in implicit_names + and projection_dict[MODEL_SPEC_ID_RECEIVER_MECH] not in implicit_names + ): + output.append( + '{0}.add_projection(projection={1}, sender={2}, receiver={3})'.format( + comp_identifer, + _generate_component_string( + projection_dict, + component_identifiers, + component_name=name, + default_type=default_edge_type + ), + parse_valid_identifier( + projection_dict[MODEL_SPEC_ID_SENDER_MECH] + ), + parse_valid_identifier( + projection_dict[MODEL_SPEC_ID_RECEIVER_MECH] + ), + ) ) - ) # add controller if it exists (must happen after projections) if controller_name is not None: From 10034a7dfe246561ab963dd8e6d81ef9c1c0b9d4 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 7 Jul 2021 22:03:29 -0400 Subject: [PATCH 215/285] JSON: parse initial value from stateful_parameters --- psyneulink/core/globals/json.py | 11 +++++++++-- psyneulink/core/globals/keywords.py | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 549f36116ef..36e23f678d1 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -199,7 +199,7 @@ from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ - MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, \ + MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE, MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, \ MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_VERSION, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE from psyneulink.core.globals.sampleiterator import SampleIterator from psyneulink.core.globals.utilities import convert_to_list, get_all_explicit_arguments, \ @@ -377,7 +377,14 @@ def _parse_parameter_value(value, component_identifiers=None, name=None, parent_ value = f'({value[1:-1]})' elif value_type is numpy.ndarray: value = f'{value[MODEL_SPEC_ID_TYPE]}({value})' - + elif MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE in value: + # is a stateful parameter with initial value + value = _parse_parameter_value( + value[MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE], + component_identifiers, + name, + parent_parameters + ) else: # it is either a Component spec or just a plain dict try: diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index a4768f1abe4..2d528cc13db 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -82,7 +82,7 @@ 'MODEL_SPEC_ID_GENERIC', 'MODEL_SPEC_ID_INPUT_PORTS', 'MODEL_SPEC_ID_OUTPUT_PORTS', 'MODEL_SPEC_ID_PSYNEULINK', 'MODEL_SPEC_ID_SENDER_MECH', 'MODEL_SPEC_ID_SENDER_PORT', 'MODEL_SPEC_ID_RECEIVER_MECH', 'MODEL_SPEC_ID_RECEIVER_PORT', 'MODEL_SPEC_ID_GENERATING_APP', 'MODEL_SPEC_ID_FORMAT', - 'MODEL_SPEC_ID_VERSION', 'MODEL_SPEC_ID_PARAMETER_SOURCE', + 'MODEL_SPEC_ID_VERSION', 'MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE', 'MODEL_SPEC_ID_PARAMETER_SOURCE', 'MODEL_SPEC_ID_PARAMETER_VALUE', 'MODEL_SPEC_ID_TYPE', 'MSE', 'MULTIPLICATIVE', 'MULTIPLICATIVE_PARAM', 'MUTUAL_ENTROPY', 'NAME', 'NESTED', 'NEWEST', 'NODE', 'NOISE', 'NORMAL_DIST_FUNCTION', 'NORMED_L0_SIMILARITY', 'NOT_EQUAL', @@ -998,6 +998,7 @@ def _is_metric(metric): MODEL_SPEC_ID_PARAMETER_SOURCE = 'source' MODEL_SPEC_ID_PARAMETER_VALUE = 'value' +MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE = 'default_initial_value' MODEL_SPEC_ID_NODES = 'nodes' MODEL_SPEC_ID_PROJECTIONS = 'edges' From 7e0eb3b4b358b0fb5017401e7f8db3f4576a99c0 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 8 Jul 2021 21:52:51 -0400 Subject: [PATCH 216/285] JSON: support determining primary function from output_ports entry --- psyneulink/core/globals/json.py | 36 ++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 36e23f678d1..0b748e3f578 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -200,7 +200,7 @@ from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE, MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, \ - MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_VERSION, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE + MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_OUTPUT_PORTS, MODEL_SPEC_ID_VERSION, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE from psyneulink.core.globals.sampleiterator import SampleIterator from psyneulink.core.globals.utilities import convert_to_list, get_all_explicit_arguments, \ parse_string_to_psyneulink_object_string, parse_valid_identifier, safe_equals, convert_to_np_array @@ -582,11 +582,37 @@ def _generate_component_string( # pnl objects only have one function unless specified in another way # than just "function" - try: - parameter_names['function'] = list(component_dict['functions'])[0] + if 'functions' in component_dict: + function_determined_by_output_port = False + + try: + output_ports = component_dict[MODEL_SPEC_ID_OUTPUT_PORTS] + except KeyError: + pass + else: + if len(output_ports) == 1 or isinstance(output_ports, list): + try: + primary_output_port = output_ports[0] + except KeyError: + primary_output_port = output_ports[list(output_ports)[0]] + function_determined_by_output_port = True + else: + try: + # 'out_port' appears to be the general primary output_port term + # should ideally have a marker in json to define it as primary + primary_output_port = output_ports['out_port'] + except KeyError: + pass + else: + function_determined_by_output_port = True + + # neuroml-style mdf has MODEL_SPEC_ID_PARAMETER_VALUE in output port definitions + if function_determined_by_output_port and MODEL_SPEC_ID_PARAMETER_VALUE in primary_output_port: + parameter_names['function'] = re.sub(r'(.*)\[\d+\]', '\\1', primary_output_port[MODEL_SPEC_ID_PARAMETER_VALUE]) + else: + parameter_names['function'] = list(component_dict['functions'])[0] + parameters['function'] = component_dict['functions'][parameter_names['function']] - except KeyError: - pass assignment_str = f'{parse_valid_identifier(name)} = ' if assignment else '' From 10ca35b077e87af11fcd003a156b2276267534a4 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 23 Jul 2021 00:35:20 -0400 Subject: [PATCH 217/285] JSON: UDF: export only name if part of known external module replace the usual dill-encoded function string with just the name of custom_function if this custom_function is part of a supported external module (currently python math and modeci_mdf standard functions) --- .../functions/userdefinedfunction.py | 33 +++++++++++++++++-- psyneulink/core/globals/utilities.py | 14 ++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index 4f0ae2dc1a8..09edeeec51e 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -17,10 +17,10 @@ from psyneulink.core.components.functions.function import FunctionError, Function_Base from psyneulink.core.globals.keywords import \ CONTEXT, CUSTOM_FUNCTION, OWNER, PARAMS, \ - SELF, USER_DEFINED_FUNCTION, USER_DEFINED_FUNCTION_TYPE + SELF, USER_DEFINED_FUNCTION, USER_DEFINED_FUNCTION_TYPE, MODEL_SPEC_ID_PSYNEULINK from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences import is_pref_set -from psyneulink.core.globals.utilities import iscompatible +from psyneulink.core.globals.utilities import _is_module_class, iscompatible from psyneulink.core import llvm as pnlvm @@ -687,3 +687,32 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, post_block = builder.append_basic_block(name="post_udf") builder.position_at_start(post_block) return builder + + @property + def _dict_summary(self): + import math + + summary = super()._dict_summary + ext_function_str = None + + try: + import modeci_mdf.functions.standard + # remove import/module errors when modeci_mdf is a package + except (ImportError, ModuleNotFoundError): + pass + else: + if self.custom_function in [ + func_dict['function'] + for name, func_dict + in modeci_mdf.functions.standard.mdf_functions.items() + ]: + ext_function_str = self.custom_function.__name__ + + if _is_module_class(self.custom_function, math): + ext_function_str = f'{self.custom_function.__module__}.{self.custom_function.__name__}' + + if ext_function_str is not None: + summary[self._model_spec_id_parameters][MODEL_SPEC_ID_PSYNEULINK]['custom_function'] = ext_function_str + summary['function'] = ext_function_str + + return summary diff --git a/psyneulink/core/globals/utilities.py b/psyneulink/core/globals/utilities.py index 98212b36c17..a77f319061c 100644 --- a/psyneulink/core/globals/utilities.py +++ b/psyneulink/core/globals/utilities.py @@ -1929,3 +1929,17 @@ def contains_type( pass return False + + +def _is_module_class(class_: type, module: types.ModuleType) -> bool: + """ + Returns: + bool: True if **class_** is a member of **module**, False otherwise + """ + if module.__name__ == class_.__module__: + try: + return class_ is getattr(module, class_.__name__) + except AttributeError: + pass + + return False From 5be4b6d870e87060e4290bc353a5add5dbc7ed84 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 28 Jul 2021 23:57:37 -0400 Subject: [PATCH 218/285] JSON: Composition: improve projection filtering - rename projection sender/receiver name of CIMs to their Compositions at the projection level - at Composition level, filter out projections to/from the Composition's CIMs and to/from nodes outside the Composition --- .../core/components/projections/projection.py | 12 +++++-- psyneulink/core/compositions/composition.py | 31 ++++++------------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 48e91ce5e49..3ea18fca917 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -1041,17 +1041,25 @@ def _model_spec_parameter_blacklist(self): @property def _dict_summary(self): + from psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism import CompositionInterfaceMechanism + # these may occur during deferred init if not isinstance(self.sender, type): sender_name = self.sender.name - sender_mech = self.sender.owner.name + if isinstance(self.sender.owner, CompositionInterfaceMechanism): + sender_mech = self.sender.owner.composition.name + else: + sender_mech = self.sender.owner.name else: sender_name = None sender_mech = None if not isinstance(self.receiver, type): receiver_name = self.receiver.name - receiver_mech = self.receiver.owner.name + if isinstance(self.receiver.owner, CompositionInterfaceMechanism): + receiver_mech = self.receiver.owner.composition.name + else: + receiver_mech = self.receiver.owner.name else: receiver_name = None receiver_mech = None diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 601f00ddc7c..02217ed34f4 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -11442,32 +11442,19 @@ def _dict_summary(self): pass for p in list(self.projections) + additional_projections: - has_cim_sender = isinstance( - p.sender.owner, - CompositionInterfaceMechanism - ) - has_cim_receiver = isinstance( - p.receiver.owner, - CompositionInterfaceMechanism - ) - - # filter projections to/from CIMs, unless they are to embedded - # compositions (any others should be automatically generated) + p_summary = p._dict_summary + # filter projections to/from CIMs of this composition + # and projections to things outside this composition if ( - (not has_cim_sender or p.sender.owner.composition in self.nodes) + ( + p_summary[MODEL_SPEC_ID_SENDER_MECH] != self.name + and p_summary[MODEL_SPEC_ID_RECEIVER_MECH] != self.name + ) and ( - not has_cim_receiver - or p.receiver.owner.composition in self.nodes + p_summary[MODEL_SPEC_ID_SENDER_MECH] in nodes_dict + or p_summary[MODEL_SPEC_ID_RECEIVER_MECH] in nodes_dict ) ): - p_summary = p._dict_summary - - if has_cim_sender: - p_summary[MODEL_SPEC_ID_SENDER_MECH] = p.sender.owner.composition.name - - if has_cim_receiver: - p_summary[MODEL_SPEC_ID_RECEIVER_MECH] = p.receiver.owner.composition.name - projections_dict[p.name] = p_summary if len(nodes_dict[MODEL_SPEC_ID_PSYNEULINK]) == 0: From e7069471f70b6f4a5c1967941d1655c4808f3de3 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 3 Aug 2021 00:56:14 -0400 Subject: [PATCH 219/285] JSON: accept full module string as type --- psyneulink/core/globals/json.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 0b748e3f578..e0fb1bcf6e7 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -315,6 +315,13 @@ def get_pnl_component_type(s): except (AttributeError, TypeError): pass + try: + eval(type_str) + except (TypeError, NameError, SyntaxError): + pass + else: + return type_str + try: from modeci_mdf.functions.standard import mdf_functions mdf_functions[type_str]['function'] From 646f576b251b057c34c6125bface26a2190a4a3e Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 3 Aug 2021 18:42:03 -0400 Subject: [PATCH 220/285] JSON: Condition: fix check for base Condition --- psyneulink/core/scheduling/condition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index b39e9ca53c0..0d91fa3dd40 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -75,7 +75,7 @@ def is_satisfied(self, *args, context=None, execution_id=None, **kwargs): def _dict_summary(self): from psyneulink.core.components.component import Component - if type(self) is graph_scheduler.Condition: + if type(self) in {graph_scheduler.Condition, Condition}: try: func_val = inspect.getsource(self.func) except OSError: From 68ea74cfe80f5320d13a203c8a0f0b2630162cb8 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 3 Aug 2021 23:57:49 -0400 Subject: [PATCH 221/285] JSON: Condition: update to named args format --- psyneulink/core/globals/json.py | 43 ++++++++++++++++-------- psyneulink/core/scheduling/condition.py | 44 +++++++++++++++++++------ 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index e0fb1bcf6e7..43a38f647cc 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -188,6 +188,7 @@ import copy import dill import enum +import inspect import json import math import numpy @@ -817,28 +818,44 @@ def _parse_condition_arg_value(value): return str(_parse_parameter_value(value, component_identifiers)) args_str = '' + sig = inspect.signature(getattr(psyneulink, condition_dict[MODEL_SPEC_ID_TYPE]).__init__) - if len(condition_dict['args']) > 0: - arg_str_list = [] - for arg in condition_dict['args']: - # handle nested Conditions - try: - arg = _generate_condition_string(arg, component_identifiers) - except TypeError: - pass + var_positional_arg_name = None + + for name, param in sig.parameters.items(): + if param.kind is inspect.Parameter.VAR_POSITIONAL: + var_positional_arg_name = name + break + + args_dict = condition_dict['args'] + + try: + pos_args = args_dict[var_positional_arg_name] + except KeyError: + pass + else: + if len(pos_args) > 0: + arg_str_list = [] + for arg in pos_args: + # handle nested Conditions + try: + arg = _generate_condition_string(arg, component_identifiers) + except TypeError: + pass - arg_str_list.append(_parse_condition_arg_value(arg)) - args_str = f", {', '.join(arg_str_list)}" + arg_str_list.append(_parse_condition_arg_value(arg)) + args_str = f", {', '.join(arg_str_list)}" kwargs_str = '' - if len(condition_dict['kwargs']) > 0: + kwargs = {k: v for k, v in args_dict.items() if k not in {'function', var_positional_arg_name}} + if len(kwargs) > 0: kwarg_str_list = [] - for key, val in condition_dict['kwargs'].items(): + for key, val in kwargs.items(): kwarg_str_list.append(f'{key}={_parse_condition_arg_value(val)}') kwargs_str = f", {', '.join(kwarg_str_list)}" arguments_str = '{0}{1}{2}'.format( - condition_dict['function'] if condition_dict['function'] is not None else '', + args_dict['function'] if args_dict['function'] is not None else '', args_str, kwargs_str ) diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index 0d91fa3dd40..dbdcd96a5cd 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -83,19 +83,43 @@ def _dict_summary(self): else: func_val = None - args_list = [] - for a in self.args: - if isinstance(a, Component): - a = a.name - elif isinstance(a, graph_scheduler.Condition): - a = a._dict_summary - args_list.append(a) + extra_args = { + 'function': func_val, + **self.kwargs, + } + + sig = inspect.signature(self.__init__) + + for name, param in sig.parameters.items(): + if param.kind is inspect.Parameter.VAR_POSITIONAL: + args_list = [] + for a in self.args: + if isinstance(a, Component): + a = a.name + elif isinstance(a, graph_scheduler.Condition): + a = a._dict_summary + args_list.append(a) + extra_args[name] = args_list + + for i, (name, param) in enumerate(filter( + lambda item: item[1].kind is inspect.Parameter.POSITIONAL_OR_KEYWORD and item[0] not in self.kwargs, + sig.parameters.items() + )): + try: + extra_args[name] = self.args[i] + except IndexError: + # was specified with keyword not as positional arg + extra_args[name] = param.default + + for name in extra_args: + if isinstance(extra_args[name], Component): + extra_args[name] = extra_args[name].name + elif isinstance(extra_args[name], graph_scheduler.Condition): + extra_args[name] = extra_args[name]._dict_summary return { MODEL_SPEC_ID_TYPE: self.__class__.__name__, - 'function': func_val, - 'args': args_list, - 'kwargs': self.kwargs, + 'args': extra_args } From 56200a77ca2e9b809401879b852fd17af7fd8f0e Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 4 Aug 2021 23:44:47 -0400 Subject: [PATCH 222/285] JSON: make more parameters pnl_internal/excluded --- psyneulink/core/components/component.py | 10 +++++----- .../functions/nonstateful/optimizationfunctions.py | 2 +- .../functions/stateful/integratorfunctions.py | 4 ++-- .../components/functions/stateful/memoryfunctions.py | 2 +- .../components/functions/stateful/statefulfunction.py | 2 +- .../core/components/functions/userdefinedfunction.py | 1 + .../mechanisms/processing/transfermechanism.py | 2 +- psyneulink/core/compositions/composition.py | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index ca573cb5595..82c6b591658 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1010,10 +1010,10 @@ class Parameters(ParametersBase): fallback_default=True, pnl_internal=True) is_finished_flag = Parameter(True, loggable=False, stateful=True) - execute_until_finished = True - num_executions = Parameter(Time(), read_only=True, modulable=False, loggable=False) - num_executions_before_finished = Parameter(0, read_only=True, modulable=False) - max_executions_before_finished = Parameter(1000, modulable=False) + execute_until_finished = Parameter(True, pnl_internal=True) + num_executions = Parameter(Time(), read_only=True, modulable=False, loggable=False, pnl_internal=True) + num_executions_before_finished = Parameter(0, read_only=True, modulable=False, pnl_internal=True) + max_executions_before_finished = Parameter(1000, modulable=False, pnl_internal=True) def _parse_variable(self, variable): if variable is None: @@ -3930,7 +3930,7 @@ def _model_spec_parameter_blacklist(self): A set of Parameter names that should not be added to the generated constructor string """ - return {'function', 'value'} + return {'function', 'value', 'execution_count', 'is_finished_flag', 'num_executions', 'num_executions_before_finished'} COMPONENT_BASE_CLASS = Component diff --git a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py index c3c23fffd95..1f70a337c64 100644 --- a/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/optimizationfunctions.py @@ -1066,7 +1066,7 @@ class Parameters(OptimizationFunction.Parameters): # these should be removed and use switched to .get_previous() previous_variable = Parameter([[0], [0]], read_only=True, pnl_internal=True, constructor_argument='default_variable') - previous_value = Parameter([[0], [0]], read_only=True, initializer='initializer', pnl_internal=True) + previous_value = Parameter([[0], [0]], read_only=True, initializer='initializer') gradient_function = Parameter(None, stateful=False, loggable=False) step_size = Parameter(1.0, modulable=True) diff --git a/psyneulink/core/components/functions/stateful/integratorfunctions.py b/psyneulink/core/components/functions/stateful/integratorfunctions.py index aa154c43f43..1cc57f1d775 100644 --- a/psyneulink/core/components/functions/stateful/integratorfunctions.py +++ b/psyneulink/core/components/functions/stateful/integratorfunctions.py @@ -217,7 +217,7 @@ class Parameters(StatefulFunction.Parameters): noise = Parameter( 0.0, modulable=True, function_arg=True, setter=_noise_setter ) - previous_value = Parameter(np.array([0]), initializer='initializer', pnl_internal=True) + previous_value = Parameter(np.array([0]), initializer='initializer') initializer = Parameter(np.array([0]), pnl_internal=True) @tc.typecheck @@ -4391,7 +4391,7 @@ class Parameters(IntegratorFunction.Parameters): # this should be removed because it's unused, but this will # require a larger refactoring on previous_value/value - previous_value = Parameter(None, initializer='initializer', pnl_internal=True) + previous_value = Parameter(None, initializer='initializer') enable_output_type_conversion = Parameter( False, diff --git a/psyneulink/core/components/functions/stateful/memoryfunctions.py b/psyneulink/core/components/functions/stateful/memoryfunctions.py index d64a1acd1c3..abade02079c 100644 --- a/psyneulink/core/components/functions/stateful/memoryfunctions.py +++ b/psyneulink/core/components/functions/stateful/memoryfunctions.py @@ -1088,7 +1088,7 @@ class Parameters(StatefulFunction.Parameters): """ variable = Parameter([[0],[0]], pnl_internal=True, constructor_argument='default_variable') initializer = Parameter(None, pnl_internal=True) - previous_value = Parameter(None, initializer='initializer', pnl_internal=True) + previous_value = Parameter(None, initializer='initializer') retrieval_prob = Parameter(1.0, modulable=True) storage_prob = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) # FIX: MAKE THESE ATTRIBUTES RATHER THAN PARAMETERS: diff --git a/psyneulink/core/components/functions/stateful/statefulfunction.py b/psyneulink/core/components/functions/stateful/statefulfunction.py index d420cd4e930..1a365aca476 100644 --- a/psyneulink/core/components/functions/stateful/statefulfunction.py +++ b/psyneulink/core/components/functions/stateful/statefulfunction.py @@ -199,7 +199,7 @@ class Parameters(Function_Base.Parameters): """ noise = Parameter(0.0, modulable=True, setter=_noise_setter) rate = Parameter(1.0, modulable=True) - previous_value = Parameter(np.array([0]), initializer='initializer', pnl_internal=True) + previous_value = Parameter(np.array([0]), initializer='initializer') initializer = Parameter(np.array([0]), pnl_internal=True) has_initializers = Parameter(True, setter=_has_initializers_setter, pnl_internal=True) diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index 09edeeec51e..a08f5686b1b 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -447,6 +447,7 @@ class Parameters(Function_Base.Parameters): None, stateful=False, loggable=False, + pnl_internal=True, ) @tc.typecheck diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index cd0fc1bcb39..5a5563a0816 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -1227,7 +1227,7 @@ class Parameters(ProcessingMechanism_Base.Parameters): ) termination_threshold = Parameter(None, modulable=True) termination_comparison_op = Parameter(LESS_THAN_OR_EQUAL, modulable=False, loggable=False) - termination_measure_value = Parameter(0.0, modulable=False, read_only=True) + termination_measure_value = Parameter(0.0, modulable=False, read_only=True, pnl_internal=True) output_ports = Parameter( [RESULTS], diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 02217ed34f4..17e1b3f221f 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -3698,7 +3698,7 @@ class Parameters(ParametersBase): """ results = Parameter([], loggable=False, pnl_internal=True) simulation_results = Parameter([], loggable=False, pnl_internal=True) - retain_old_simulation_data = Parameter(False, stateful=False, loggable=False) + retain_old_simulation_data = Parameter(False, stateful=False, loggable=False, pnl_internal=True) input_specification = Parameter(None, stateful=False, loggable=False, pnl_internal=True) From 290e38ceb476e95fd73d4faf1a47619c46b327f7 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 4 Aug 2021 23:45:44 -0400 Subject: [PATCH 223/285] JSON: fix extra parameters in UDF init --- psyneulink/core/globals/json.py | 105 ++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 43a38f647cc..9bc9207b45d 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -654,6 +654,55 @@ def _generate_component_string( except KeyError: pass + def parameter_value_matches_default(component_type, param, value): + default_val = getattr(component_type.defaults, param) + evaled_val = NotImplemented + + # see if val is a psyneulink class instantiation + # if so, do not instantiate it (avoid offsetting rng for + # testing - see if you can bypass another way?) + try: + eval(re.match(r'(psyneulink\.\w+)\(', value).group(1)) + is_pnl_instance = True + except (AttributeError, TypeError, NameError, ValueError): + is_pnl_instance = False + + if not is_pnl_instance: + # val may be a string that evaluates to the default value + # also skip listing in constructor in this case + try: + evaled_val = eval(value) + except (TypeError, NameError, ValueError): + pass + except Exception: + # Assume this occurred in creation of a Component + # that probably needs some hidden/automatic modification. + # Special handling here? + # still relevant after testing for instance above? + pass + + # skip specifying parameters that match the class defaults + if ( + not safe_equals(value, default_val) + and ( + evaled_val is NotImplemented + or not safe_equals(evaled_val, default_val) + ) + ): + # test for dill use/equivalence + try: + is_dill_str = value[:5] == 'dill.' + except TypeError: + is_dill_str = False + + if ( + not is_dill_str + or dill.dumps(eval(value)) != dill.dumps(default_val) + ): + return False + + return True + # sort on arg name for arg, val in sorted(parameters.items(), key=lambda p: p[0]): try: @@ -681,59 +730,21 @@ def _generate_component_string( except KeyError: val = _parse_parameter_value(val, component_identifiers, parent_parameters=parent_parameters) - default_val = getattr(component_type.defaults, arg) - - evaled_val = NotImplemented - - # see if val is a psyneulink class instantiation - # if so, do not instantiate it (avoid offsetting rng for - # testing - see if you can bypass another way?) - try: - eval(re.match(r'(psyneulink\.\w+)\(', val).group(1)) - is_pnl_instance = True - except (AttributeError, TypeError, NameError, ValueError): - is_pnl_instance = False - - if not is_pnl_instance: - # val may be a string that evaluates to the default value - # also skip listing in constructor in this case - try: - evaled_val = eval(val) - except (TypeError, NameError, ValueError): - pass - except Exception: - # Assume this occurred in creation of a Component - # that probably needs some hidden/automatic modification. - # Special handling here? - # still relevant after testing for instance above? - pass - - # skip specifying parameters that match the class defaults - if ( - not safe_equals(val, default_val) - and ( - evaled_val is NotImplemented - or not safe_equals(evaled_val, default_val) - ) - ): - # test for dill use/equivalence - try: - is_dill_str = val[:5] == 'dill.' - except TypeError: - is_dill_str = False - - if ( - not is_dill_str - or dill.dumps(eval(val)) != dill.dumps(default_val) - ): - additional_arguments.append(f'{constructor_arg}={val}') + if not parameter_value_matches_default(component_type, arg, val): + additional_arguments.append(f'{constructor_arg}={val}') elif component_type is UserDefinedFunction: if arg != MODEL_SPEC_ID_MDF_VARIABLE: val = _parse_parameter_value( val, component_identifiers, parent_parameters=parent_parameters ) - additional_arguments.append(f'{constructor_arg}={val}') + try: + matches = parameter_value_matches_default(component_type, arg, val) + except AttributeError: + matches = False + + if not matches: + additional_arguments.append(f'{constructor_arg}={val}') output = '{0}psyneulink.{1}{2}{3}{4}'.format( assignment_str, From 99eeb152f77e85b816b2d312839a56452e30f56a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Mon, 9 Aug 2021 21:04:06 -0400 Subject: [PATCH 224/285] JSON: pass string expression as component_type --- psyneulink/core/globals/json.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 9bc9207b45d..98251deb0e4 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -316,13 +316,6 @@ def get_pnl_component_type(s): except (AttributeError, TypeError): pass - try: - eval(type_str) - except (TypeError, NameError, SyntaxError): - pass - else: - return type_str - try: from modeci_mdf.functions.standard import mdf_functions mdf_functions[type_str]['function'] @@ -339,6 +332,15 @@ def get_pnl_component_type(s): else: return f'math.{type_str}' + try: + eval(type_str) + except (TypeError, SyntaxError): + pass + except NameError: + return type_str + else: + return type_str + raise PNLJSONError( 'Invalid type specified for JSON object: {0}'.format( component_dict From 2a61bb7996c77c6e1e418f668698ac871b685db4 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 10 Aug 2021 01:04:17 -0400 Subject: [PATCH 225/285] JSON: support UDF stateful parameter --- psyneulink/core/globals/json.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 98251deb0e4..82fb1c2dd4f 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -735,6 +735,15 @@ def parameter_value_matches_default(component_type, param, value): if not parameter_value_matches_default(component_type, arg, val): additional_arguments.append(f'{constructor_arg}={val}') elif component_type is UserDefinedFunction: + try: + val[MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE] + except (KeyError, TypeError): + pass + else: + # is a stateful parameter corresponding to this function + if val[MODEL_SPEC_ID_PARAMETER_VALUE] == name: + additional_arguments.append(f"stateful_parameter='{arg}'") + if arg != MODEL_SPEC_ID_MDF_VARIABLE: val = _parse_parameter_value( val, component_identifiers, parent_parameters=parent_parameters From b614c4a07f7bafde13191580348ae9765159feb8 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 10 Aug 2021 01:06:26 -0400 Subject: [PATCH 226/285] JSON: pass stateful parameters to node functions --- psyneulink/core/globals/json.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 82fb1c2dd4f..a7a067a4a3f 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -641,6 +641,11 @@ def _generate_component_string( if parent_parameters is None: parent_parameters = parameters + parameters = { + **{k: v for k, v in parent_parameters.items() if isinstance(v, dict) and MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE in v}, + **parameters + } + if 'variable' not in parameters: try: ip = parameters['function'][Function_Base._model_spec_id_parameters][MODEL_SPEC_ID_MDF_VARIABLE] From 82275a72a2f4f09e2358633c9e3c48dc043922c0 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 2 Sep 2021 21:34:39 -0400 Subject: [PATCH 227/285] LinearMatrix: add mdf equivalent names --- .../components/functions/nonstateful/transferfunctions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/functions/nonstateful/transferfunctions.py b/psyneulink/core/components/functions/nonstateful/transferfunctions.py index d35deceaa5d..d47aff7c673 100644 --- a/psyneulink/core/components/functions/nonstateful/transferfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/transferfunctions.py @@ -2870,6 +2870,8 @@ class LinearMatrix(TransferFunction): # --------------------------------------- DEFAULT_FILLER_VALUE = 0 + _model_spec_generic_type_name = 'onnx::MatMul' + class Parameters(TransferFunction.Parameters): """ Attributes @@ -2881,7 +2883,8 @@ class Parameters(TransferFunction.Parameters): :default value: None :type: """ - matrix = Parameter(None, modulable=True) + variable = Parameter(np.array([0]), read_only=True, pnl_internal=True, constructor_argument='default_variable', mdf_name='A') + matrix = Parameter(None, modulable=True, mdf_name='B') bounds = None # def is_matrix_spec(m): From 2659590f56acabf74c7bdfb90230c92f94dda259 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 3 Sep 2021 21:23:19 -0400 Subject: [PATCH 228/285] JSON: Composition: support excluded_node_roles --- psyneulink/core/components/component.py | 2 +- psyneulink/core/globals/json.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 82c6b591658..935f9c8062b 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -3702,7 +3702,7 @@ def parse_parameter_value(value): # attributes that aren't Parameters but are psyneulink-specific # and are stored in the PNL parameters section - implicit_parameter_attributes = ['node_ordering', 'required_node_roles'] + implicit_parameter_attributes = ['node_ordering', 'required_node_roles', 'excluded_node_roles'] parameters_dict = {} pnl_specific_parameters = {} diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index a7a067a4a3f..56c7a6a0473 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -1148,6 +1148,14 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): except KeyError: node_roles = [] + try: + excluded_node_roles = { + parse_valid_identifier(node): role for (node, role) in + composition_dict[comp_type._model_spec_id_parameters][MODEL_SPEC_ID_PSYNEULINK]['excluded_node_roles'] + } + except KeyError: + excluded_node_roles = [] + # do not add the controller as a normal node try: controller_name = composition_dict['controller']['name'] @@ -1181,6 +1189,13 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): if len(composition_dict[MODEL_SPEC_ID_NODES]) > 0: output.append('') + if len(excluded_node_roles) > 0: + for node, roles in excluded_node_roles.items(): + output.append( + f'{comp_identifer}.exclude_node_roles({node}, {_parse_parameter_value(roles, component_identifiers)})' + ) + output.append('') + try: edges_dict = composition_dict[MODEL_SPEC_ID_PROJECTIONS] except KeyError: From 3c8c4be70d99b4d506b87cef5cbfd802684af758 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 24 Sep 2021 23:45:26 -0400 Subject: [PATCH 229/285] tests/json: fix failures due to results shape mismatch actual differences in results were suppressed if numpy == comparison fails --- tests/json/test_json.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/json/test_json.py b/tests/json/test_json.py index 3276ec05696..308784ae8f0 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -58,7 +58,7 @@ def test_json_results_equivalence( exec(pnl.generate_script_from_json(json_summary)) exec(f'{composition_name}.run(inputs={input_dict_str})') new_results = eval(f'{composition_name}.results') - assert orig_results == new_results + assert pnl.safe_equals(orig_results, new_results) @pytest.mark.parametrize( @@ -87,7 +87,7 @@ def test_write_json_file( # exec(f'{composition_name}.run(inputs={input_dict_str})') exec(f'pnl.get_compositions()[0].run(inputs={input_dict_str})') final_results = eval(f'{composition_name}.results') - assert orig_results == final_results + assert pnl.safe_equals(orig_results, final_results) @pytest.mark.parametrize( From 1b24a49794b4c10949f7b9b37e34642e9814dca2 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 17 Mar 2022 18:36:16 -0400 Subject: [PATCH 230/285] tests/json: remove extra code in model_with_control --- tests/json/model_with_control.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/json/model_with_control.py b/tests/json/model_with_control.py index 3dbf877b3db..54b1ac053ad 100644 --- a/tests/json/model_with_control.py +++ b/tests/json/model_with_control.py @@ -70,5 +70,3 @@ ], ) ) -comp._analyze_graph() -assert True From 65967d9e1f67891b4bbb771ed5670cea5ea2eac3 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Mon, 14 Mar 2022 21:28:29 -0400 Subject: [PATCH 231/285] ci: increase fetch depth and tags --- .github/workflows/pnl-ci-docs.yml | 8 ++++++-- .github/workflows/pnl-ci.yml | 10 +++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pnl-ci-docs.yml b/.github/workflows/pnl-ci-docs.yml index eabd0b8d508..15eaf6f60a4 100644 --- a/.github/workflows/pnl-ci-docs.yml +++ b/.github/workflows/pnl-ci-docs.yml @@ -39,20 +39,24 @@ jobs: on_master: ${{ steps.on_master.outputs.on-branch }} steps: + # increased fetch-depth and tag checkout needed as in pnl-ci.yml - name: Checkout sources uses: actions/checkout@v3 if: ${{ matrix.pnl-version == 'head' }} with: - fetch-depth: 10 + fetch-depth: 200 ref: ${{ github.ref }} - name: Checkout pull base uses: actions/checkout@v3 if: ${{ matrix.pnl-version == 'base' }} with: - fetch-depth: 10 + fetch-depth: 200 ref: ${{ github.base_ref }} + - name: Checkout tags + run: git fetch --tags origin master + - name: Check if on master if: ${{ github.event_name == 'push' }} id: on_master diff --git a/.github/workflows/pnl-ci.yml b/.github/workflows/pnl-ci.yml index 5794cd75646..4e1088a429b 100644 --- a/.github/workflows/pnl-ci.yml +++ b/.github/workflows/pnl-ci.yml @@ -40,10 +40,18 @@ jobs: os: macos-latest steps: + # increased fetch-depth and tag checkout needed to get correct + # version string from versioneer (must have history to a prior tag); + # otherwise install fails due to circular dependency with modeci_mdf - name: Checkout sources uses: actions/checkout@v3 with: - fetch-depth: 10 + fetch-depth: 200 + + # fetch only master to avoid getting unneeded branches with + # characters invalid on windows + - name: Checkout tags + run: git fetch --tags origin master - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 From c0a67c5fff5c0a228650286d5aa94aacca528136 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 26 Oct 2021 16:07:49 -0400 Subject: [PATCH 232/285] JSON: use MDF classes for import/export add as_mdf_model method where _dict_summary currently existed, replace use of _dict_summary. _dict_summary is not deleted due to current lack of support for nested Graphs in MDF (TBD if support for this is desired) requirements: include modeci_mdf package at <0.3.2 tests: add tests for MDF results equivalence Parameters: add mdf_name entry --- psyneulink/core/components/component.py | 240 +++++++++++++++++- .../core/components/functions/function.py | 23 +- .../nonstateful/transferfunctions.py | 13 + .../functions/userdefinedfunction.py | 29 +++ .../core/components/mechanisms/mechanism.py | 33 ++- psyneulink/core/components/ports/inputport.py | 11 +- .../core/components/ports/outputport.py | 22 +- .../core/components/projections/projection.py | 58 ++++- psyneulink/core/compositions/composition.py | 70 ++++- psyneulink/core/globals/json.py | 170 ++++++++++--- psyneulink/core/globals/keywords.py | 9 +- psyneulink/core/globals/parameters.py | 2 + psyneulink/core/scheduling/condition.py | 64 +++++ psyneulink/core/scheduling/scheduler.py | 12 + requirements.txt | 1 + tests/json/model_udfs.py | 23 ++ tests/json/test_json.py | 43 +++- 17 files changed, 762 insertions(+), 61 deletions(-) create mode 100644 tests/json/model_udfs.py diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 935f9c8062b..24fadd54576 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -508,6 +508,7 @@ import dill import graph_scheduler +import modeci_mdf.mdf as mdf import numpy as np from psyneulink.core import llvm as pnlvm @@ -518,8 +519,9 @@ CONTEXT, CONTROL_PROJECTION, DEFERRED_INITIALIZATION, EXECUTE_UNTIL_FINISHED, \ FUNCTION, FUNCTION_PARAMS, INIT_FULL_EXECUTE_METHOD, INPUT_PORTS, \ LEARNING, LEARNING_PROJECTION, MATRIX, MAX_EXECUTIONS_BEFORE_FINISHED, \ - MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PARAMETER_SOURCE, \ + MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_METADATA, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PARAMETER_SOURCE, \ MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_OUTPUT_PORTS, \ + MODEL_SPEC_ID_MDF_VARIABLE, \ MODULATORY_SPEC_KEYWORDS, NAME, OUTPUT_PORTS, OWNER, PARAMS, PREFS_ARG, \ RESET_STATEFUL_FUNCTION_WHEN, VALUE, VARIABLE from psyneulink.core.globals.log import LogCondition @@ -533,7 +535,7 @@ from psyneulink.core.globals.utilities import \ ContentAddressableList, convert_all_elements_to_np_array, convert_to_np_array, get_deepcopy_with_shared, \ is_instance_or_subclass, is_matrix, iscompatible, kwCompatibilityLength, prune_unused_args, \ - get_all_explicit_arguments, call_with_pruned_args, safe_equals, safe_len + get_all_explicit_arguments, call_with_pruned_args, safe_equals, safe_len, parse_valid_identifier from psyneulink.core.scheduling.condition import Never from psyneulink.core.scheduling.time import Time, TimeScale @@ -3692,7 +3694,12 @@ def parse_parameter_value(value): # in fact this would happen unless the parser specifically # handles it like ours does value = value._dict_summary - elif isinstance(value, (types.FunctionType)): + elif isinstance(value, types.FunctionType): + try: + if value is getattr(eval(value.__module__.replace('numpy', 'np')), value.__qualname__): + return f'{value.__module__}.{value.__qualname__}' + except (AttributeError, NameError, TypeError): + pass value = base64.encodebytes(dill.dumps(value)).decode('utf-8') return value @@ -3807,6 +3814,233 @@ def parse_parameter_value(value): **{MODEL_SPEC_ID_TYPE: type_dict} } + def _get_mdf_parameters(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.functions.nonstateful.transferfunctions import LinearMatrix + + def parse_parameter_value(value, no_expand_components=False, functions_as_dill=False): + if isinstance(value, (list, tuple)): + try: + # treat as a collections.namedtuple + type(value)._fields + except AttributeError: + pass + else: + # cannot expect MDF/neuromllite to handle our namedtuples + value = tuple(value) + + new_item = [] + for item in value: + new_item.append( + parse_parameter_value( + item, + no_expand_components, + functions_as_dill + ) + ) + try: + value = type(value)(new_item) + except TypeError: + value = type(value)(*new_item) + elif isinstance(value, dict): + value = { + parse_parameter_value(k, no_expand_components, functions_as_dill): parse_parameter_value(v, no_expand_components, functions_as_dill) + for k, v in value.items() + } + elif isinstance(value, Composition): + value = value.name + elif isinstance(value, Port): + if isinstance(value, OutputPort): + state_port_name = MODEL_SPEC_ID_OUTPUT_PORTS + else: + state_port_name = MODEL_SPEC_ID_INPUT_PORTS + + # assume we will use the identifier on reconstitution + value = '{0}.{1}.{2}'.format( + value.owner.name, + state_port_name, + value.name + ) + elif isinstance(value, Component): + # could potentially create duplicates when it should + # create a reference to an already existent Component like + # with Compositions, but in a vacuum the full specification + # is necessary. + # in fact this would happen unless the parser specifically + # handles it like ours does + if no_expand_components: + value = parse_valid_identifier(value.name) + else: + value = value.as_mdf_model() + elif isinstance(value, ComponentsMeta): + value = value.__name__ + elif isinstance(value, (type, types.BuiltinFunctionType)): + if value.__module__ == 'builtins': + # just give standard type, like float or int + value = f'{value.__name__}' + elif value is np.ndarray: + value = f'{value.__module__}.array' + else: + # some builtin modules are internally "_module" + # but are imported with "module" + value = f"{value.__module__.lstrip('_')}.{value.__name__}" + elif isinstance(value, types.MethodType): + if isinstance(value.__self__, Component): + # assume reference to a method on a Component is + # automatically assigned (may not be totally + # accurate but is in current known cases) + value = None + else: + value = value.__qualname__ + elif isinstance(value, types.FunctionType): + if functions_as_dill: + value = base64.encodebytes(dill.dumps(value)).decode('utf-8') + elif '.' in value.__qualname__: + value = value.__qualname__ + else: + try: + if value is getattr(eval(value.__module__.replace('numpy', 'np')), value.__qualname__): + return f'{value.__module__}.{value.__qualname__}' + except (AttributeError, NameError, TypeError): + pass + + value = str(value) + elif isinstance(value, SampleIterator): + value = f'{value.__class__.__name__}({repr(value.specification)})' + elif value is NotImplemented: + value = None + # IntEnum gets treated as int + elif isinstance(value, (Enum, types.SimpleNamespace)): + value = str(value) + elif not isinstance(value, (float, int, str, bool, mdf.Base, type(None), np.ndarray)): + value = str(value) + + return value + + # attributes that aren't Parameters but are psyneulink-specific + # and are stored in the PNL parameters section + implicit_parameter_attributes = ['node_ordering', 'required_node_roles'] + + parameters_dict = {} + pnl_specific_parameters = {} + deferred_init_values = {} + + if self.initialization_status is ContextFlags.DEFERRED_INIT: + deferred_init_values = copy.copy(self._init_args) + try: + deferred_init_values.update(deferred_init_values['params']) + except (KeyError, TypeError): + pass + + # .parameters still refers to class parameters during deferred init + assert self.parameters._owner is not self + + for p in self.parameters: + if ( + p.name not in self._model_spec_parameter_blacklist + and not isinstance(p, (ParameterAlias, SharedParameter)) + ): + if self.initialization_status is ContextFlags.DEFERRED_INIT: + try: + val = deferred_init_values[p.name] + except KeyError: + # class default + val = p.default_value + else: + # special handling because LinearMatrix default values + # can be PNL-specific keywords. In future, generalize + # this workaround + if ( + isinstance(self, LinearMatrix) + and p.name == 'matrix' + ): + val = self.parameters.matrix.values[None] + elif p.spec is not None: + val = p.spec + else: + val = None + if not p.stateful and not p.structural: + val = p.get(None) + if val is None: + val = p.default_value + + val = parse_parameter_value(val, functions_as_dill=True) + + # split parameters designated as PsyNeuLink-specific and + # parameters that are universal + if p.pnl_internal: + target_param_dict = pnl_specific_parameters + else: + target_param_dict = parameters_dict + + if p.mdf_name is not None: + target_param_dict[p.mdf_name] = val + else: + target_param_dict[p.name] = val + + for attr in implicit_parameter_attributes: + try: + pnl_specific_parameters[attr] = parse_parameter_value(getattr(self, attr), no_expand_components=True, functions_as_dill=True) + except AttributeError: + pass + + if len(pnl_specific_parameters) > 0: + parameters_dict[MODEL_SPEC_ID_PSYNEULINK] = pnl_specific_parameters + + return {self._model_spec_id_parameters: {k: v for k, v in parameters_dict.items()}} + + @property + def _mdf_model_parameters(self): + params = self._get_mdf_parameters() + try: + del params[self._model_spec_id_parameters][MODEL_SPEC_ID_PSYNEULINK] + except KeyError: + pass + + params[self._model_spec_id_parameters] = { + k: v for k, v in params[self._model_spec_id_parameters].items() + if ( + k == MODEL_SPEC_ID_MDF_VARIABLE + or ( + isinstance(v, (numbers.Number, np.ndarray)) + and not isinstance(v, bool) + ) + ) + } + + return params + + @property + def _mdf_metadata(self): + all_parameters = self._get_mdf_parameters() + try: + params = all_parameters[self._model_spec_id_parameters][MODEL_SPEC_ID_PSYNEULINK] + except KeyError: + params = {} + + params = { + **params, + **{ + k: v for k, v in all_parameters[self._model_spec_id_parameters].items() + if ( + k not in {MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_PSYNEULINK} + and ( + not isinstance(v, (numbers.Number, np.ndarray)) + or isinstance(v, bool) + ) + ) + } + } + + return { + MODEL_SPEC_ID_METADATA: { + 'type': type(self).__name__, + **params + } + } + @property def logged_items(self): """Dictionary of all items that have entries in the log, and their currently assigned `ContextFlags`\\s diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index 7f291fa3818..095c0840217 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -146,6 +146,8 @@ import warnings from enum import Enum, IntEnum +import modeci_mdf.mdf as mdf +import modeci_mdf.functions.standard as mdf_functions import numpy as np import typecheck as tc @@ -163,7 +165,7 @@ from psyneulink.core.globals.preferences.preferenceset import PreferenceEntry, PreferenceLevel from psyneulink.core.globals.registry import register_category from psyneulink.core.globals.utilities import ( - convert_to_np_array, get_global_seed, object_has_single_value, parameter_spec, safe_len, + convert_to_np_array, get_global_seed, object_has_single_value, parameter_spec, parse_valid_identifier, safe_len, SeededRandomState, contains_type, is_instance_or_subclass ) @@ -829,6 +831,25 @@ def _dict_summary(self): 'function': type_str.lower() } + def as_mdf_model(self): + if self._model_spec_generic_type_name is not NotImplemented: + typ = self._model_spec_generic_type_name + else: + try: + typ = self.custom_function.__name__ + except AttributeError: + typ = type(self).__name__.lower() + + if typ not in mdf_functions.mdf_functions: + warnings.warn(f'{typ} is not an MDF standard function, this is likely to produce an incompatible model.') + + return mdf.Function( + id=parse_valid_identifier(self.name), + function=typ, + **self._mdf_model_parameters, + **self._mdf_metadata + ) + # ***************************************** EXAMPLE FUNCTION ******************************************************* PROPENSITY = "PROPENSITY" diff --git a/psyneulink/core/components/functions/nonstateful/transferfunctions.py b/psyneulink/core/components/functions/nonstateful/transferfunctions.py index d47aff7c673..c1dcb5cb684 100644 --- a/psyneulink/core/components/functions/nonstateful/transferfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/transferfunctions.py @@ -1042,6 +1042,19 @@ def derivative(self, input=None, output=None, context=None): return gain * scale * output * (1 - output) + def as_mdf_model(self): + model = super().as_mdf_model() + # x_0 is included in bias in MDF logistic + model.args['bias'] = model.args['bias'] - model.args['x_0'] + model.args['x_0'] = 0 + + if model.args['scale'] != 1.0: + warnings.warn( + f"Scale (set to {model.args['scale']} is not a supported" + ' parameter for MDF logistic' + ) + return model + # ********************************************************************************************************************** # Tanh diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index a08f5686b1b..05a34779a29 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -717,3 +717,32 @@ def _dict_summary(self): summary['function'] = ext_function_str return summary + + def as_mdf_model(self): + import math + + model = super().as_mdf_model() + ext_function_str = None + + try: + import modeci_mdf.functions.standard + # remove import/module errors when modeci_mdf is a package + except (ImportError, ModuleNotFoundError): + pass + else: + if self.custom_function in [ + func_dict['function'] + for name, func_dict + in modeci_mdf.functions.standard.mdf_functions.items() + ]: + ext_function_str = self.custom_function.__name__ + + if _is_module_class(self.custom_function, math): + ext_function_str = f'{self.custom_function.__module__}.{self.custom_function.__name__}' + + if ext_function_str is not None: + model.metadata['custom_function'] = ext_function_str + model.function = ext_function_str + del model.metadata['type'] + + return model diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 1103c7b9272..d792721f825 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1083,6 +1083,7 @@ from inspect import isclass from numbers import Number +import modeci_mdf.mdf as mdf import numpy as np import typecheck as tc @@ -1107,13 +1108,13 @@ MULTIPLICATIVE_PARAM, EXECUTION_COUNT, \ NAME, OUTPUT, OUTPUT_LABELS_DICT, OUTPUT_PORT, OUTPUT_PORT_PARAMS, OUTPUT_PORTS, OWNER_EXECUTION_COUNT, OWNER_VALUE, \ PARAMETER_PORT, PARAMETER_PORT_PARAMS, PARAMETER_PORTS, PROJECTIONS, REFERENCE_VALUE, RESULT, \ - TARGET_LABELS_DICT, VALUE, VARIABLE, WEIGHT + TARGET_LABELS_DICT, VALUE, VARIABLE, WEIGHT, MODEL_SPEC_ID_MDF_VARIABLE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.registry import register_category, remove_instance_from_registry from psyneulink.core.globals.utilities import \ ContentAddressableList, append_type_to_name, convert_all_elements_to_np_array, convert_to_np_array, \ - iscompatible, kwCompatibilityNumeric, convert_to_list + iscompatible, kwCompatibilityNumeric, convert_to_list, parse_valid_identifier from psyneulink.core.scheduling.condition import Condition from psyneulink.core.scheduling.time import TimeScale @@ -4119,6 +4120,34 @@ def _dict_summary(self): **outputs_dict } + def as_mdf_model(self): + model = mdf.Node( + id=parse_valid_identifier(self.name), + **self._mdf_metadata, + ) + + for name, val in self._mdf_model_parameters[self._model_spec_id_parameters].items(): + model.parameters.append(mdf.Parameter(id=name, value=val)) + + for ip in self.input_ports: + ip_model = ip.as_mdf_model() + ip_model.id = f'{parse_valid_identifier(self.name)}_{ip_model.id}' + + model.input_ports.append(ip_model) + + for op in self.output_ports: + op_model = op.as_mdf_model() + op_model.id = f'{parse_valid_identifier(self.name)}_{op_model.id}' + + model.output_ports.append(op_model) + + function_model = self.function.as_mdf_model() + # primary input port + function_model.args[MODEL_SPEC_ID_MDF_VARIABLE] = model.input_ports[0].id + model.functions.append(function_model) + + return model + def _is_mechanism_spec(spec): """Evaluate whether spec is a valid Mechanism specification diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index d8a0b6ed816..3efb064d15e 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -565,6 +565,7 @@ import numbers import warnings +import modeci_mdf.mdf as mdf import numpy as np import typecheck as tc @@ -584,7 +585,7 @@ from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.utilities import \ - append_type_to_name, convert_to_np_array, is_numeric, iscompatible, kwCompatibilityLength, convert_to_list + append_type_to_name, convert_to_np_array, is_numeric, iscompatible, kwCompatibilityLength, convert_to_list, parse_valid_identifier __all__ = [ 'InputPort', 'InputPortError', 'port_type_keywords', 'SHADOW_INPUTS', @@ -1466,6 +1467,14 @@ def _get_port_function_value(owner, function, variable): return Port_Base._get_port_function_value(owner=owner, function=function, variable=variable) + def as_mdf_model(self): + return mdf.InputPort( + id=parse_valid_identifier(self.name), + shape=str(self.defaults.variable.shape), + type=str(self.defaults.variable.dtype), + **self._mdf_metadata + ) + def _instantiate_input_ports(owner, input_ports=None, reference_value=None, context=None): """Call Port._instantiate_port_list() to instantiate ContentAddressableList of InputPort(s) diff --git a/psyneulink/core/components/ports/outputport.py b/psyneulink/core/components/ports/outputport.py index ed86defa1bc..9fbcf7b4ab4 100644 --- a/psyneulink/core/components/ports/outputport.py +++ b/psyneulink/core/components/ports/outputport.py @@ -621,6 +621,8 @@ import numpy as np import typecheck as tc +import modeci_mdf.mdf as mdf + from psyneulink.core.components.component import Component, ComponentError from psyneulink.core.components.functions.function import Function from psyneulink.core.components.ports.port import Port_Base, _instantiate_port_list, port_type_keywords @@ -635,7 +637,7 @@ from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.utilities import \ - convert_to_np_array, is_numeric, iscompatible, make_readonly_property, recursive_update + convert_to_np_array, is_numeric, iscompatible, make_readonly_property, recursive_update, parse_valid_identifier __all__ = [ 'OutputPort', 'OutputPortError', 'PRIMARY', 'SEQUENTIAL', 'StandardOutputPorts', 'StandardOutputPortsError', @@ -1302,6 +1304,23 @@ def _dict_summary(self): } } + def as_mdf_model(self): + owner_func_name = parse_valid_identifier(self.owner.function.name) + if self._variable_spec == OWNER_VALUE: + value = owner_func_name + elif isinstance(self._variable_spec, tuple) and self._variable_spec[0] == OWNER_VALUE: + if len(self.owner.defaults.value) == 1: + value = owner_func_name + else: + value = f'{owner_func_name}[{self._variable_spec[1]}]' + else: + raise ValueError(f'Unsupported variable spec for MDF: {self._variable_spec}') + + return mdf.OutputPort( + id=parse_valid_identifier(self.name), + value=value, + **self._mdf_metadata + ) def _instantiate_output_ports(owner, output_ports=None, context=None): """Call Port._instantiate_port_list() to instantiate ContentAddressableList of OutputPort(s) @@ -1656,6 +1675,7 @@ def names(self): # def indices(self): # return [item[INDEX] for item in self.data] + def _parse_output_port_function(owner, output_port_name, function, params_dict_as_variable=False): """Parse specification of function as Function, Function class, Function.function, types.FunctionType or types.MethodType. diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 3ea18fca917..f32f34ab569 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -398,6 +398,7 @@ import warnings from collections import namedtuple, defaultdict +import modeci_mdf.mdf as mdf import numpy as np import typecheck as tc @@ -412,7 +413,7 @@ CONTROL, CONTROL_PROJECTION, CONTROL_SIGNAL, EXPONENT, FUNCTION_PARAMS, GATE, GATING_PROJECTION, GATING_SIGNAL, \ INPUT_PORT, LEARNING, LEARNING_PROJECTION, LEARNING_SIGNAL, \ MAPPING_PROJECTION, MATRIX, MATRIX_KEYWORD_SET, MECHANISM, \ - MODEL_SPEC_ID_RECEIVER_MECH, MODEL_SPEC_ID_RECEIVER_PORT, MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_SENDER_PORT, \ + MODEL_SPEC_ID_RECEIVER_MECH, MODEL_SPEC_ID_RECEIVER_PORT, MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_SENDER_PORT, MODEL_SPEC_ID_METADATA, \ NAME, OUTPUT_PORT, OUTPUT_PORTS, PARAMS, PATHWAY, PROJECTION, PROJECTION_PARAMS, PROJECTION_SENDER, PROJECTION_TYPE, \ RECEIVER, SENDER, STANDARD_ARGS, PORT, PORTS, WEIGHT, ADD_INPUT_PORT, ADD_OUTPUT_PORT, \ PROJECTION_COMPONENT_CATEGORY @@ -420,7 +421,7 @@ from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.registry import register_category, remove_instance_from_registry from psyneulink.core.globals.socket import ConnectionInfo -from psyneulink.core.globals.utilities import ContentAddressableList, is_matrix, is_numeric +from psyneulink.core.globals.utilities import ContentAddressableList, is_matrix, is_numeric, parse_valid_identifier __all__ = [ 'Projection_Base', 'projection_keywords', 'PROJECTION_SPEC_KEYWORDS', @@ -1076,6 +1077,59 @@ def _dict_summary(self): **socket_dict } + def as_mdf_model(self): + from psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism import CompositionInterfaceMechanism + + # these may occur during deferred init + if not isinstance(self.sender, type): + sender_name = parse_valid_identifier(self.sender.name) + if isinstance(self.sender.owner, CompositionInterfaceMechanism): + sender_mech = parse_valid_identifier(self.sender.owner.composition.name) + else: + sender_mech = parse_valid_identifier(self.sender.owner.name) + else: + sender_name = None + sender_mech = None + + if not isinstance(self.receiver, type): + receiver_name = parse_valid_identifier(self.receiver.name) + if isinstance(self.receiver.owner, CompositionInterfaceMechanism): + receiver_mech = parse_valid_identifier(self.receiver.owner.composition.name) + else: + receiver_mech = parse_valid_identifier(self.receiver.owner.name) + else: + receiver_name = None + receiver_mech = None + + socket_dict = { + MODEL_SPEC_ID_SENDER_PORT: f'{sender_mech}_{sender_name}', + MODEL_SPEC_ID_RECEIVER_PORT: f'{receiver_mech}_{receiver_name}', + MODEL_SPEC_ID_SENDER_MECH: sender_mech, + MODEL_SPEC_ID_RECEIVER_MECH: receiver_mech + } + + parameters = self._mdf_model_parameters + if self.defaults.weight is None: + parameters[self._model_spec_id_parameters]['weight'] = 1 + + metadata = self._mdf_metadata + try: + metadata[MODEL_SPEC_ID_METADATA]['functions'] = mdf.Function.to_dict_format( + self.function.as_mdf_model(), + ordered=False + ) + except AttributeError: + # projection is in deferred init, special handling here? + pass + + return mdf.Edge( + id=parse_valid_identifier(self.name), + **socket_dict, + **parameters, + **metadata + ) + + @tc.typecheck def _is_projection_spec(spec, proj_type:tc.optional(type)=None, include_matrix_spec=True): """Evaluate whether spec is a valid Projection specification diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 17e1b3f221f..50e9dabc407 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2706,6 +2706,7 @@ def input_function(env, result): from typing import Union import graph_scheduler +import modeci_mdf.mdf as mdf import networkx import numpy as np import pint @@ -2753,7 +2754,7 @@ def input_function(env, result): DICT, FEEDBACK, FULL, FUNCTION, HARD_CLAMP, IDENTITY_MATRIX, INPUT, INPUT_PORTS, INPUTS, INPUT_CIM_NAME, \ LEARNED_PROJECTIONS, LEARNING_FUNCTION, LEARNING_MECHANISM, LEARNING_MECHANISMS, LEARNING_PATHWAY, \ MATRIX, MATRIX_KEYWORD_VALUES, MAYBE, \ - MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, \ + MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_METADATA, \ MODEL_SPEC_ID_RECEIVER_MECH, MODEL_SPEC_ID_SENDER_MECH, \ MONITOR, MONITOR_FOR_CONTROL, NAME, NESTED, NO_CLAMP, NODE, OBJECTIVE_MECHANISM, ONLINE, OUTCOME, \ OUTPUT, OUTPUT_CIM_NAME, OUTPUT_MECHANISM, OUTPUT_PORTS, OWNER_VALUE, \ @@ -2767,7 +2768,7 @@ def input_function(env, result): from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel, _assign_prefs from psyneulink.core.globals.registry import register_category from psyneulink.core.globals.utilities import \ - ContentAddressableList, call_with_pruned_args, convert_to_list, nesting_depth, convert_to_np_array, is_numeric + ContentAddressableList, call_with_pruned_args, convert_to_list, nesting_depth, convert_to_np_array, is_numeric, parse_valid_identifier from psyneulink.core.scheduling.condition import All, AllHaveRun, Always, Any, Condition, Never from psyneulink.core.scheduling.scheduler import Scheduler, SchedulingMode from psyneulink.core.scheduling.time import Time, TimeScale @@ -11479,6 +11480,71 @@ def _dict_summary(self): # endregion LLVM + def as_mdf_model(self): + """Creates a ModECI MDF Model representing this Composition + + Args: + simple_edge_format (bool, optional): If True, Projections + with non-identity matrices are constructed as . Defaults to True. + + Returns: + modeci_mdf.Model: a ModECI Model representing this Composition + """ + nodes_dict = {} + projections_dict = {} + self_identifier = parse_valid_identifier(self.name) + metadata = self._mdf_metadata + + additional_projections = [] + additional_nodes = ( + [self.controller] + if self.controller is not None + else [] + ) + + for n in list(self.nodes) + additional_nodes: + if not isinstance(n, CompositionInterfaceMechanism): + nodes_dict[parse_valid_identifier(n.name)] = n.as_mdf_model() + + # consider making this more general in the future + try: + additional_projections.extend(n.control_projections) + except AttributeError: + pass + + for p in list(self.projections) + additional_projections: + p_model = p.as_mdf_model() + # filter projections to/from CIMs of this composition + # and projections to things outside this composition + if ( + ( + p_model.sender != self_identifier + and p_model.receiver != self_identifier + ) + and ( + p_model.sender in nodes_dict + or p_model.receiver in nodes_dict + ) + ): + projections_dict[parse_valid_identifier(p.name)] = p_model + + metadata[MODEL_SPEC_ID_METADATA]['controller'] = self.controller.as_mdf_model() if self.controller is not None else None + + graph = mdf.Graph( + id=self_identifier, + conditions=self.scheduler.as_mdf_model(), + **self._mdf_model_parameters, + **metadata + ) + + for _, node in nodes_dict.items(): + graph.nodes.append(node) + + for _, proj in projections_dict.items(): + graph.edges.append(proj) + + return graph + # ****************************************************************************************************************** # region ----------------------------------- PROPERTIES ------------------------------------------------------------ # ****************************************************************************************************************** diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 56c7a6a0473..8fdcb37ac83 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -191,6 +191,8 @@ import inspect import json import math +import modeci_mdf.mdf as mdf +import numbers import numpy import pickle import pint @@ -201,7 +203,8 @@ from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE, MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, \ - MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_GENERATING_APP, MODEL_SPEC_ID_FORMAT, MODEL_SPEC_ID_OUTPUT_PORTS, MODEL_SPEC_ID_VERSION, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE + MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_OUTPUT_PORTS, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE, MODEL_SPEC_ID_METADATA +from psyneulink.core.globals.parameters import ParameterAlias from psyneulink.core.globals.sampleiterator import SampleIterator from psyneulink.core.globals.utilities import convert_to_list, get_all_explicit_arguments, \ parse_string_to_psyneulink_object_string, parse_valid_identifier, safe_equals, convert_to_np_array @@ -266,6 +269,8 @@ def default(self, o): return f'numpy.random.RandomState({o.seed})' elif isinstance(o, numpy.number): return o.item() + elif isinstance(o, mdf.BaseWithId): + return json.loads(o.to_json()) try: return super().default(o) @@ -298,17 +303,25 @@ def get_pnl_component_type(s): # if matching component not found, raise original exception raise - try: + type_str = None + if MODEL_SPEC_ID_TYPE in component_dict: type_dict = component_dict[MODEL_SPEC_ID_TYPE] - except KeyError: - # specifically for functions the keyword is not 'type' - type_str = component_dict['function'] else: + try: + type_dict = component_dict[MODEL_SPEC_ID_METADATA][MODEL_SPEC_ID_TYPE] + except KeyError: + # specifically for functions the keyword is not 'type' + type_str = component_dict['function'] + + if type_str is None: try: type_str = type_dict[MODEL_SPEC_ID_PSYNEULINK] except KeyError: # catch error outside of this function if necessary type_str = type_dict[MODEL_SPEC_ID_GENERIC] + except TypeError: + # actually a str + type_str = type_dict try: # gets the actual psyneulink type (Component, etc..) from the module @@ -355,8 +368,18 @@ def _parse_parameter_value(value, component_identifiers=None, name=None, parent_ exec('import numpy') if isinstance(value, list): - value = [_parse_parameter_value(x, component_identifiers, name, parent_parameters) for x in value] - value = f"[{', '.join([str(x) for x in value])}]" + new_val = [_parse_parameter_value(x, component_identifiers, name, parent_parameters) for x in value] + + # check for ParameterPort spec + if ( + len(value) == 2 + and isinstance(value[0], (numbers.Number, numpy.ndarray)) + and isinstance(value[1], dict) + ): + # make tuple instead of list + value = f"({', '.join([str(x) for x in new_val])})" + else: + value = f"[{', '.join([str(x) for x in new_val])}]" elif isinstance(value, dict): if ( MODEL_SPEC_ID_PARAMETER_SOURCE in value @@ -395,6 +418,14 @@ def _parse_parameter_value(value, component_identifiers=None, name=None, parent_ name, parent_parameters ) + elif MODEL_SPEC_ID_PARAMETER_VALUE in value: + # is a standard mdf Parameter class with value + value = _parse_parameter_value( + value[MODEL_SPEC_ID_PARAMETER_VALUE], + component_identifiers, + name, + parent_parameters + ) else: # it is either a Component spec or just a plain dict try: @@ -533,6 +564,7 @@ def _generate_component_string( ): from psyneulink.core.components.functions.function import Function_Base from psyneulink.core.components.functions.userdefinedfunction import UserDefinedFunction + from psyneulink.core.components.projections.projection import Projection_Base try: component_type = _parse_component_type(component_dict) @@ -555,9 +587,15 @@ def _generate_component_string( except KeyError: pass + is_user_defined_function = False try: parameters = dict(component_dict[component_type._model_spec_id_parameters]) except AttributeError: + is_user_defined_function = True + except KeyError: + parameters = {} + + if is_user_defined_function or component_type is UserDefinedFunction: custom_func = component_type component_type = UserDefinedFunction try: @@ -566,11 +604,9 @@ def _generate_component_string( pass parameters['custom_function'] = f'{custom_func}' try: - del parameters[MODEL_SPEC_ID_PSYNEULINK]['custom_function'] + del component_dict[MODEL_SPEC_ID_METADATA]['custom_function'] except KeyError: pass - except KeyError: - parameters = {} try: parameters.update(component_dict[component_type._model_spec_id_stateful_parameters]) @@ -590,6 +626,17 @@ def _generate_component_string( except KeyError: pass + try: + metadata = component_dict[MODEL_SPEC_ID_METADATA] + except KeyError: + metadata = {} + + if issubclass(component_type, Projection_Base): + try: + component_dict['functions'] = metadata['functions'] + except KeyError: + pass + # pnl objects only have one function unless specified in another way # than just "function" if 'functions' in component_dict: @@ -622,7 +669,9 @@ def _generate_component_string( else: parameter_names['function'] = list(component_dict['functions'])[0] - parameters['function'] = component_dict['functions'][parameter_names['function']] + parameters['function'] = { + parameter_names['function']: component_dict['functions'][parameter_names['function']] + } assignment_str = f'{parse_valid_identifier(name)} = ' if assignment else '' @@ -643,10 +692,15 @@ def _generate_component_string( parameters = { **{k: v for k, v in parent_parameters.items() if isinstance(v, dict) and MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE in v}, - **parameters + **parameters, + **metadata } - if 'variable' not in parameters: + # MDF input ports do not have functions, so their shape is + # equivalent to ours after the InputPort function is run (this + # function may change the shape of the default variable), so ignore + # the input port shape if input_ports parameter is specified + if 'variable' not in parameters and 'input_ports' not in parameters: try: ip = parameters['function'][Function_Base._model_spec_id_parameters][MODEL_SPEC_ID_MDF_VARIABLE] var = convert_to_np_array( @@ -710,8 +764,18 @@ def parameter_value_matches_default(component_type, param, value): return True + mdf_names_to_pnl = { + p.mdf_name: p.name for p in component_type.parameters + if p.mdf_name is not None and not isinstance(p, ParameterAlias) + } + # sort on arg name for arg, val in sorted(parameters.items(), key=lambda p: p[0]): + try: + arg = mdf_names_to_pnl[arg] + except KeyError: + pass + try: constructor_parameter_name = getattr(component_type.parameters, arg).constructor_argument # Some Parameters may be stored just to be replicated here, and @@ -737,7 +801,10 @@ def parameter_value_matches_default(component_type, param, value): except KeyError: val = _parse_parameter_value(val, component_identifiers, parent_parameters=parent_parameters) - if not parameter_value_matches_default(component_type, arg, val): + if ( + (arg in component_type.parameters or constructor_arg in component_type.parameters) + and not parameter_value_matches_default(component_type, arg, val) + ): additional_arguments.append(f'{constructor_arg}={val}') elif component_type is UserDefinedFunction: try: @@ -881,8 +948,13 @@ def _parse_condition_arg_value(value): kwarg_str_list.append(f'{key}={_parse_condition_arg_value(val)}') kwargs_str = f", {', '.join(kwarg_str_list)}" + if 'function' in args_dict and args_dict['function'] is not None: + func_str = args_dict['function'] + else: + func_str = '' + arguments_str = '{0}{1}{2}'.format( - args_dict['function'] if args_dict['function'] is not None else '', + func_str, args_str, kwargs_str ) @@ -935,11 +1007,6 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): # may be given multiple compositions for comp_name, composition_dict in graphs_dict.items(): - try: - comp_type = _parse_component_type(composition_dict) - except KeyError: - comp_type = default_composition_type - try: assert comp_name == composition_dict['name'] except KeyError: @@ -947,15 +1014,34 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): comp_identifer = parse_valid_identifier(comp_name) + def alphabetical_order(items): + alphabetical = enumerate( + sorted(items) + ) + return { + parse_valid_identifier(item[1]): item[0] + for item in alphabetical + } + # get order in which nodes were added # may be node names or dictionaries try: - node_order = composition_dict[comp_type._model_spec_id_parameters][MODEL_SPEC_ID_PSYNEULINK]['node_ordering'] + node_order = composition_dict[MODEL_SPEC_ID_METADATA]['node_ordering'] node_order = { - parse_valid_identifier(node['name']) if isinstance(node, dict) + parse_valid_identifier(list(node.keys())[0]) if isinstance(node, dict) else parse_valid_identifier(node): node_order.index(node) for node in node_order } + + unspecified_node_order = { + node: position + len(node_order) + for node, position in alphabetical_order([ + n for n in composition_dict[MODEL_SPEC_ID_NODES] if n not in node_order + ]).items() + } + + node_order.update(unspecified_node_order) + assert all([ (parse_valid_identifier(node) in node_order) for node in composition_dict[MODEL_SPEC_ID_NODES] @@ -963,13 +1049,7 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): except (KeyError, TypeError, AssertionError): # if no node_ordering attribute exists, fall back to # alphabetical order - alphabetical = enumerate( - sorted(composition_dict[MODEL_SPEC_ID_NODES]) - ) - node_order = { - parse_valid_identifier(item[1]): item[0] - for item in alphabetical - } + node_order = alphabetical_order(composition_dict[MODEL_SPEC_ID_NODES]) # clean up pnl-specific and other software-specific items pnl_specific_items = {} @@ -1143,7 +1223,7 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): try: node_roles = { parse_valid_identifier(node): role for (node, role) in - composition_dict[comp_type._model_spec_id_parameters][MODEL_SPEC_ID_PSYNEULINK]['required_node_roles'] + composition_dict[MODEL_SPEC_ID_METADATA]['required_node_roles'] } except KeyError: node_roles = [] @@ -1151,17 +1231,15 @@ def _replace_function_node_with_mech_node(function_dict, name, typ=None): try: excluded_node_roles = { parse_valid_identifier(node): role for (node, role) in - composition_dict[comp_type._model_spec_id_parameters][MODEL_SPEC_ID_PSYNEULINK]['excluded_node_roles'] + composition_dict[MODEL_SPEC_ID_METADATA]['excluded_node_roles'] } except KeyError: excluded_node_roles = [] # do not add the controller as a normal node try: - controller_name = composition_dict['controller']['name'] - except TypeError: - controller_name = composition_dict['controller'] - except KeyError: + controller_name = list(composition_dict[MODEL_SPEC_ID_METADATA]['controller'].keys())[0] + except (AttributeError, KeyError, TypeError): controller_name = None for name in sorted( @@ -1430,6 +1508,7 @@ def generate_json(*compositions): specifies `Composition` or iterable of ones to be output in JSON """ + import modeci_mdf from psyneulink.core.compositions.composition import Composition model_name = "_".join([c.name for c in compositions]) @@ -1448,13 +1527,20 @@ def generate_json(*compositions): except KeyError: merged_graphs_dict_summary.update(c._dict_summary) - return _dump_pnl_json_from_dict({ - model_name: { - MODEL_SPEC_ID_FORMAT: MODEL_SPEC_ID_VERSION, - MODEL_SPEC_ID_GENERATING_APP: f'psyneulink v{psyneulink.__version__}', - **merged_graphs_dict_summary - } - }) + model = mdf.Model( + id=model_name, + format=f'ModECI MDF v{modeci_mdf.__version__}', + generating_application=f'PsyNeuLink v{psyneulink.__version__}', + ) + + for c in compositions: + if not isinstance(c, Composition): + raise PNLJSONError( + f'Item in compositions arg of {__name__}() is not a Composition: {c}.' + ) + model.graphs.append(c.as_mdf_model()) + + return model.to_json() def write_json_file(compositions, filename:str, path:str=None): diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index 2d528cc13db..281ad2bfed7 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -81,8 +81,8 @@ 'MODULATORY_SIGNALS', 'MONITOR', 'MONITOR_FOR_CONTROL', 'MONITOR_FOR_LEARNING', 'MONITOR_FOR_MODULATION', 'MODEL_SPEC_ID_GENERIC', 'MODEL_SPEC_ID_INPUT_PORTS', 'MODEL_SPEC_ID_OUTPUT_PORTS', 'MODEL_SPEC_ID_PSYNEULINK', 'MODEL_SPEC_ID_SENDER_MECH', 'MODEL_SPEC_ID_SENDER_PORT', - 'MODEL_SPEC_ID_RECEIVER_MECH', 'MODEL_SPEC_ID_RECEIVER_PORT', 'MODEL_SPEC_ID_GENERATING_APP', 'MODEL_SPEC_ID_FORMAT', - 'MODEL_SPEC_ID_VERSION', 'MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE', 'MODEL_SPEC_ID_PARAMETER_SOURCE', + 'MODEL_SPEC_ID_RECEIVER_MECH', 'MODEL_SPEC_ID_RECEIVER_PORT', + 'MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE', 'MODEL_SPEC_ID_PARAMETER_SOURCE', 'MODEL_SPEC_ID_PARAMETER_VALUE', 'MODEL_SPEC_ID_TYPE', 'MSE', 'MULTIPLICATIVE', 'MULTIPLICATIVE_PARAM', 'MUTUAL_ENTROPY', 'NAME', 'NESTED', 'NEWEST', 'NODE', 'NOISE', 'NORMAL_DIST_FUNCTION', 'NORMED_L0_SIMILARITY', 'NOT_EQUAL', @@ -980,13 +980,10 @@ def _is_metric(metric): #endregion # model spec keywords -MODEL_SPEC_ID_GENERATING_APP = 'generating_application' -MODEL_SPEC_ID_FORMAT = 'format' -MODEL_SPEC_ID_VERSION = 'ModECI MDF v0.1' - MODEL_SPEC_ID_TYPE = 'type' MODEL_SPEC_ID_PSYNEULINK = 'PNL' MODEL_SPEC_ID_GENERIC = 'generic' +MODEL_SPEC_ID_METADATA = 'metadata' MODEL_SPEC_ID_INPUT_PORTS = 'input_ports' MODEL_SPEC_ID_OUTPUT_PORTS = 'output_ports' diff --git a/psyneulink/core/globals/parameters.py b/psyneulink/core/globals/parameters.py index 424c7f95e8b..d7cc7233a38 100644 --- a/psyneulink/core/globals/parameters.py +++ b/psyneulink/core/globals/parameters.py @@ -860,6 +860,7 @@ def __init__( dependencies=None, initializer=None, port=None, + mdf_name=None, _owner=None, _inherited=False, # this stores a reference to the Parameter object that is the @@ -923,6 +924,7 @@ def __init__( dependencies=dependencies, initializer=initializer, port=port, + mdf_name=mdf_name, _inherited=_inherited, _inherited_source=_inherited_source, _user_specified=_user_specified, diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index dbdcd96a5cd..c5c46367445 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -12,15 +12,19 @@ import copy import functools import inspect +import numbers import warnings import dill import graph_scheduler +import modeci_mdf.mdf as mdf import numpy as np + from psyneulink.core.globals.context import handle_external_context from psyneulink.core.globals.json import JSONDumpable from psyneulink.core.globals.keywords import MODEL_SPEC_ID_TYPE, comparison_operators from psyneulink.core.globals.parameters import parse_context +from psyneulink.core.globals.utilities import parse_valid_identifier __all__ = copy.copy(graph_scheduler.condition.__all__) __all__.extend(['Threshold']) @@ -122,6 +126,66 @@ def _dict_summary(self): 'args': extra_args } + def as_mdf_model(self): + from psyneulink.core.components.component import Component + + def _parse_condition_arg(arg): + if isinstance(arg, Component): + return parse_valid_identifier(arg.name) + elif isinstance(arg, graph_scheduler.Condition): + return arg.as_mdf_model() + elif arg is None or isinstance(arg, numbers.Number): + return arg + else: + try: + iter(arg) + except TypeError: + return str(arg) + else: + return arg + + if type(self) in {graph_scheduler.Condition, Condition}: + try: + func_val = inspect.getsource(self.func) + except OSError: + func_val = dill.dumps(self.func) + func_dict = {'function': func_val} + else: + func_dict = {} + + extra_args = {MODEL_SPEC_ID_TYPE: self.__class__.__name__} + + sig = inspect.signature(self.__init__) + + for name, param in sig.parameters.items(): + if param.kind is inspect.Parameter.VAR_POSITIONAL: + args_list = [] + for a in self.args: + if isinstance(a, Component): + a = parse_valid_identifier(a.name) + elif isinstance(a, graph_scheduler.Condition): + a = a.as_mdf_model() + args_list.append(a) + extra_args[name] = args_list + + for i, (name, param) in enumerate(filter( + lambda item: item[1].kind is inspect.Parameter.POSITIONAL_OR_KEYWORD and item[0] not in self.kwargs, + sig.parameters.items() + )): + try: + extra_args[name] = self.args[i] + except IndexError: + # was specified with keyword not as positional arg + extra_args[name] = param.default + + return mdf.Condition( + **func_dict, + **{ + k: _parse_condition_arg(v) + for k, v in (*self.kwargs.items(), *extra_args.items()) + } + ) + # below produces psyneulink versions of each Condition class so that # they are compatible with the extra changes made in Condition above diff --git a/psyneulink/core/scheduling/scheduler.py b/psyneulink/core/scheduling/scheduler.py index 28e8c9b48e0..4138c090e40 100644 --- a/psyneulink/core/scheduling/scheduler.py +++ b/psyneulink/core/scheduling/scheduler.py @@ -10,11 +10,13 @@ import typing import graph_scheduler +import modeci_mdf.mdf as mdf import pint from psyneulink import _unit_registry from psyneulink.core.globals.context import Context, handle_external_context from psyneulink.core.globals.json import JSONDumpable +from psyneulink.core.globals.utilities import parse_valid_identifier from psyneulink.core.scheduling.condition import _create_as_pnl_condition __all__ = [ @@ -108,6 +110,16 @@ def _dict_summary(self): } } + def as_mdf_model(self): + return mdf.ConditionSet( + node_specific={ + parse_valid_identifier(n.name): self.conditions[n].as_mdf_model() for n in self.nodes if n in self.conditions + }, + termination={ + str.lower(k.name): v.as_mdf_model() for k, v in self.termination_conds.items() + }, + ) + @handle_external_context() def get_clock(self, context): return super().get_clock(context.execution_id) diff --git a/requirements.txt b/requirements.txt index f2659519dcc..088c7d7ec7d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ grpcio<1.43.0 grpcio-tools<1.43.0 llvmlite<0.39 matplotlib<3.5.2 +modeci_mdf<0.3.2 networkx<2.8 numpy<1.21.4, >=1.17.0 pillow<9.1.0 diff --git a/tests/json/model_udfs.py b/tests/json/model_udfs.py new file mode 100644 index 00000000000..6fb3cfba841 --- /dev/null +++ b/tests/json/model_udfs.py @@ -0,0 +1,23 @@ +import graph_scheduler +import modeci_mdf.functions.standard + +import psyneulink as pnl + +A = pnl.TransferMechanism(name='A') +B = pnl.ProcessingMechanism( + name='B', + function=pnl.UserDefinedFunction( + modeci_mdf.functions.standard.mdf_functions['sin']['function'], + scale=1 + ) +) +C = pnl.ProcessingMechanism( + name='C', + function=pnl.UserDefinedFunction( + modeci_mdf.functions.standard.mdf_functions['cos']['function'], + scale=1 + ) +) +comp = pnl.Composition(name='comp', pathways=[A, B, C]) +comp.scheduler.add_condition(B, pnl.EveryNCalls(A, 2)) +comp.scheduler.add_condition(C, graph_scheduler.EveryNCalls(B, 2)) diff --git a/tests/json/test_json.py b/tests/json/test_json.py index 308784ae8f0..0e2230778b8 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -3,6 +3,9 @@ import psyneulink as pnl import pytest +from modeci_mdf.utils import load_mdf +import modeci_mdf.execution_engine as ee + # stroop stimuli red = [1, 0] green = [0, 1] @@ -19,7 +22,13 @@ json_results_parametrization = [ ('model_basic.py', 'comp', '{A: 1}'), - ('model_nested_comp_with_scheduler.py', 'comp', '{A: 1}'), + ('model_udfs.py', 'comp', '{A: 10}'), + pytest.param( + 'model_nested_comp_with_scheduler.py', + 'comp', + '{A: 1}', + marks=pytest.mark.xfail(reason='Nested Graphs not supported in MDF') + ), ( 'model_with_control.py', 'comp', @@ -129,3 +138,35 @@ def test_write_json_file_multiple_comps( exec(f'{composition_name}.run(inputs={input_dict_strs[composition_name]})') final_results = eval(f'{composition_name}.results') assert orig_results[composition_name] == final_results, f'{composition_name}:' + + +@pytest.mark.parametrize( + 'filename, composition_name, input_dict', + [ + ('model_basic.py', 'comp', {'A': 1}), + ('model_udfs.py', 'comp', {'A': 10}) + ] +) +def test_mdf_equivalence(filename, composition_name, input_dict): + # Get python script from file and execute + filename = f'{os.path.dirname(__file__)}/{filename}' + with open(filename, 'r') as orig_file: + exec(orig_file.read()) + inputs_str = str(input_dict).replace("'", '') + exec(f'{composition_name}.run(inputs={inputs_str})') + orig_results = eval(f'{composition_name}.results') + + # Save json_summary of Composition to file and read back in. + json_filename = filename.replace('.py', '.json') + pnl.write_json_file(eval(composition_name), json_filename) + + m = load_mdf(json_filename) + eg = ee.EvaluableGraph(m.graphs[0], verbose=True) + eg.evaluate(initializer={f'{node}_InputPort_0': i for node, i in input_dict.items()}) + + mdf_results = [ + [eo.curr_value for _, eo in eg.enodes[node.id].evaluable_outputs.items()] + for node in eg.scheduler.consideration_queue[-1] + ] + + assert pnl.safe_equals(orig_results, mdf_results) From 7a30e231cd93ca6c8a10e3d6935446e16d1365ef Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 1 Sep 2021 00:41:01 -0400 Subject: [PATCH 233/285] JSON: make projections/edges as dummy nodes with functions --- psyneulink/core/components/component.py | 5 +- .../core/components/mechanisms/mechanism.py | 5 +- .../core/components/projections/projection.py | 77 +++++++++++++++---- psyneulink/core/compositions/composition.py | 42 ++++++---- psyneulink/core/globals/json.py | 18 ++++- tests/json/test_json.py | 45 +++++++---- 6 files changed, 141 insertions(+), 51 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 24fadd54576..9a227008141 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -3873,7 +3873,10 @@ def parse_parameter_value(value, no_expand_components=False, functions_as_dill=F if no_expand_components: value = parse_valid_identifier(value.name) else: - value = value.as_mdf_model() + try: + value = value.as_mdf_model(simple_edge_format=False) + except TypeError: + value = value.as_mdf_model() elif isinstance(value, ComponentsMeta): value = value.__name__ elif isinstance(value, (type, types.BuiltinFunctionType)): diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index d792721f825..42878a142bf 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1099,6 +1099,7 @@ REMOVE_PORTS, PORT_SPEC, _parse_port_spec, PORT_SPECIFIC_PARAMS, PROJECTION_SPECIFIC_PARAMS from psyneulink.core.components.shellclasses import Mechanism, Projection, Port from psyneulink.core.globals.context import Context, ContextFlags, handle_external_context +from psyneulink.core.globals.json import _get_variable_parameter_name # TODO: remove unused keywords from psyneulink.core.globals.keywords import \ ADDITIVE_PARAM, EXECUTION_PHASE, EXPONENT, FUNCTION_PARAMS, \ @@ -1108,7 +1109,7 @@ MULTIPLICATIVE_PARAM, EXECUTION_COUNT, \ NAME, OUTPUT, OUTPUT_LABELS_DICT, OUTPUT_PORT, OUTPUT_PORT_PARAMS, OUTPUT_PORTS, OWNER_EXECUTION_COUNT, OWNER_VALUE, \ PARAMETER_PORT, PARAMETER_PORT_PARAMS, PARAMETER_PORTS, PROJECTIONS, REFERENCE_VALUE, RESULT, \ - TARGET_LABELS_DICT, VALUE, VARIABLE, WEIGHT, MODEL_SPEC_ID_MDF_VARIABLE + TARGET_LABELS_DICT, VALUE, VARIABLE, WEIGHT from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.registry import register_category, remove_instance_from_registry @@ -4143,7 +4144,7 @@ def as_mdf_model(self): function_model = self.function.as_mdf_model() # primary input port - function_model.args[MODEL_SPEC_ID_MDF_VARIABLE] = model.input_ports[0].id + function_model.args[_get_variable_parameter_name(self.function)] = model.input_ports[0].id model.functions.append(function_model) return model diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index f32f34ab569..2fb0e7421e6 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -404,11 +404,13 @@ from psyneulink.core import llvm as pnlvm from psyneulink.core.components.functions.function import get_matrix +from psyneulink.core.components.mechanisms.processing.processingmechanism import ProcessingMechanism from psyneulink.core.components.functions.nonstateful.transferfunctions import LinearMatrix from psyneulink.core.components.ports.modulatorysignals.modulatorysignal import _is_modulatory_spec from psyneulink.core.components.ports.port import PortError from psyneulink.core.components.shellclasses import Mechanism, Process_Base, Projection, Port from psyneulink.core.globals.context import ContextFlags +from psyneulink.core.globals.json import _get_variable_parameter_name from psyneulink.core.globals.keywords import \ CONTROL, CONTROL_PROJECTION, CONTROL_SIGNAL, EXPONENT, FUNCTION_PARAMS, GATE, GATING_PROJECTION, GATING_SIGNAL, \ INPUT_PORT, LEARNING, LEARNING_PROJECTION, LEARNING_SIGNAL, \ @@ -1077,7 +1079,7 @@ def _dict_summary(self): **socket_dict } - def as_mdf_model(self): + def as_mdf_model(self, simple_edge_format=True): from psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism import CompositionInterfaceMechanism # these may occur during deferred init @@ -1112,22 +1114,67 @@ def as_mdf_model(self): if self.defaults.weight is None: parameters[self._model_spec_id_parameters]['weight'] = 1 - metadata = self._mdf_metadata - try: - metadata[MODEL_SPEC_ID_METADATA]['functions'] = mdf.Function.to_dict_format( - self.function.as_mdf_model(), - ordered=False + if simple_edge_format: + edge_node = ProcessingMechanism( + name=f'{self.name}_dummy_node', + default_variable=self.defaults.variable, + function=self.function + ) + edge_function = edge_node.function + edge_node = edge_node.as_mdf_model() + + func_model = [f for f in edge_node.functions if f.id == parse_valid_identifier(edge_function.name)][0] + var_name = _get_variable_parameter_name(edge_function) + + # 2d variable on LinearMatrix will be incorrect on import back to psyneulink + func_model.metadata[var_name] = func_model.metadata[var_name][-1] + + pre_edge = mdf.Edge( + id=parse_valid_identifier(f'{self.name}_dummy_pre_edge'), + # assume weight applied before function + **{ + self._model_spec_id_parameters: { + 'weight': parameters[self._model_spec_id_parameters]['weight'] + }, + MODEL_SPEC_ID_SENDER_PORT: f'{sender_mech}_{sender_name}', + MODEL_SPEC_ID_RECEIVER_PORT: edge_node.input_ports[0].id, + MODEL_SPEC_ID_SENDER_MECH: sender_mech, + MODEL_SPEC_ID_RECEIVER_MECH: edge_node.id + } ) - except AttributeError: - # projection is in deferred init, special handling here? - pass - return mdf.Edge( - id=parse_valid_identifier(self.name), - **socket_dict, - **parameters, - **metadata - ) + for name, value in parameters[self._model_spec_id_parameters].items(): + if name not in {'weight'}: + edge_node.parameters.append(mdf.Parameter(id=name, value=value)) + edge_node.metadata['orig_metadata'] = self._mdf_metadata[MODEL_SPEC_ID_METADATA] + + post_edge = mdf.Edge( + id=parse_valid_identifier(f'{self.name}_dummy_post_edge'), + **{ + MODEL_SPEC_ID_SENDER_PORT: edge_node.output_ports[0].id, + MODEL_SPEC_ID_RECEIVER_PORT: f'{receiver_mech}_{receiver_name}', + MODEL_SPEC_ID_SENDER_MECH: edge_node.id, + MODEL_SPEC_ID_RECEIVER_MECH: receiver_mech + } + ) + return pre_edge, edge_node, post_edge + else: + metadata = self._mdf_metadata + try: + metadata[MODEL_SPEC_ID_METADATA]['functions'] = mdf.Function.to_dict_format( + self.function.as_mdf_model(), + ordered=False + ) + except AttributeError: + # projection is in deferred init, special handling here? + pass + + return mdf.Edge( + id=parse_valid_identifier(self.name), + **socket_dict, + **parameters, + **metadata + ) @tc.typecheck diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 50e9dabc407..27c99de08e3 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2724,7 +2724,7 @@ def input_function(env, result): from psyneulink.core.components.functions.nonstateful.transferfunctions import Identity from psyneulink.core.components.mechanisms.mechanism import Mechanism_Base, MechanismError, MechanismList from psyneulink.core.components.mechanisms.modulatory.control.controlmechanism import ControlMechanism -from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP +from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import AGENT_REP, OptimizationControlMechanism from psyneulink.core.components.mechanisms.modulatory.learning.learningmechanism import \ LearningMechanism, ACTIVATION_INPUT_INDEX, ACTIVATION_OUTPUT_INDEX, ERROR_SIGNAL, ERROR_SIGNAL_INDEX from psyneulink.core.components.mechanisms.modulatory.modulatorymechanism import ModulatoryMechanism_Base @@ -11480,7 +11480,7 @@ def _dict_summary(self): # endregion LLVM - def as_mdf_model(self): + def as_mdf_model(self, simple_edge_format=True): """Creates a ModECI MDF Model representing this Composition Args: @@ -11490,6 +11490,17 @@ def as_mdf_model(self): Returns: modeci_mdf.Model: a ModECI Model representing this Composition """ + def is_included_projection(proj): + included_types = ( + CompositionInterfaceMechanism, + LearningMechanism, + OptimizationControlMechanism, + ) + return ( + not isinstance(proj.sender.owner, included_types) + and not isinstance(proj.receiver.owner, included_types) + and not isinstance(proj, (AutoAssociativeProjection, ControlProjection)) + ) nodes_dict = {} projections_dict = {} self_identifier = parse_valid_identifier(self.name) @@ -11513,20 +11524,23 @@ def as_mdf_model(self): pass for p in list(self.projections) + additional_projections: - p_model = p.as_mdf_model() # filter projections to/from CIMs of this composition # and projections to things outside this composition - if ( - ( - p_model.sender != self_identifier - and p_model.receiver != self_identifier - ) - and ( - p_model.sender in nodes_dict - or p_model.receiver in nodes_dict - ) - ): - projections_dict[parse_valid_identifier(p.name)] = p_model + if is_included_projection(p): + try: + pre_edge, edge_node, post_edge = p.as_mdf_model(simple_edge_format) + except TypeError: + edges = [p.as_mdf_model(simple_edge_format)] + else: + nodes_dict[edge_node.id] = edge_node + edges = [pre_edge, post_edge] + if 'excluded_node_roles' not in metadata[MODEL_SPEC_ID_METADATA]: + metadata[MODEL_SPEC_ID_METADATA]['excluded_node_roles'] = [] + + metadata[MODEL_SPEC_ID_METADATA]['excluded_node_roles'].append([edge_node.id, str(NodeRole.OUTPUT)]) + + for e in edges: + projections_dict[e.id] = e metadata[MODEL_SPEC_ID_METADATA]['controller'] = self.controller.as_mdf_model() if self.controller is not None else None diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 8fdcb37ac83..2ea6f574911 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -288,6 +288,16 @@ def _dump_pnl_json_from_dict(dict_summary): ) +def _get_variable_parameter_name(obj): + try: + if obj.parameters.variable.mdf_name is not None: + return obj.parameters.variable.mdf_name + except AttributeError: + pass + + return MODEL_SPEC_ID_MDF_VARIABLE + + def _parse_component_type(component_dict): def get_pnl_component_type(s): from psyneulink.core.components.component import ComponentsMeta @@ -1490,7 +1500,7 @@ def get_declared_identifiers(graphs_dict): return model_output -def generate_json(*compositions): +def generate_json(*compositions, simple_edge_format=True): """ Generate the `general JSON format ` for one or more `Compositions ` and associated @@ -1538,12 +1548,12 @@ def generate_json(*compositions): raise PNLJSONError( f'Item in compositions arg of {__name__}() is not a Composition: {c}.' ) - model.graphs.append(c.as_mdf_model()) + model.graphs.append(c.as_mdf_model(simple_edge_format=simple_edge_format)) return model.to_json() -def write_json_file(compositions, filename:str, path:str=None): +def write_json_file(compositions, filename:str, path:str=None, simple_edge_format=True): """ Write one or more `Compositions ` and associated objects to file in the `general JSON format ` @@ -1572,4 +1582,4 @@ def write_json_file(compositions, filename:str, path:str=None): compositions = convert_to_list(compositions) with open(filename, 'w') as json_file: - json_file.write(generate_json(*compositions)) + json_file.write(generate_json(*compositions, simple_edge_format=simple_edge_format)) diff --git a/tests/json/test_json.py b/tests/json/test_json.py index 0e2230778b8..d821bd4993d 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -21,36 +21,49 @@ json_results_parametrization = [ - ('model_basic.py', 'comp', '{A: 1}'), - ('model_udfs.py', 'comp', '{A: 10}'), + ('model_basic.py', 'comp', '{A: 1}', True), + ('model_basic.py', 'comp', '{A: 1}', False), + ('model_udfs.py', 'comp', '{A: 10}', True), + ('model_udfs.py', 'comp', '{A: 10}', False), pytest.param( 'model_nested_comp_with_scheduler.py', 'comp', '{A: 1}', + True, + marks=pytest.mark.xfail(reason='Nested Graphs not supported in MDF') + ), + pytest.param( + 'model_nested_comp_with_scheduler.py', + 'comp', + '{A: 1}', + False, marks=pytest.mark.xfail(reason='Nested Graphs not supported in MDF') ), ( 'model_with_control.py', 'comp', - '{Input: [0.5, 0.123], reward: [20, 20]}' + '{Input: [0.5, 0.123], reward: [20, 20]}', + False ), ( 'stroop_conflict_monitoring.py', 'Stroop_model', - str(stroop_stimuli).replace("'", '') + str(stroop_stimuli).replace("'", ''), + False ), - ('model_backprop.py', 'comp', '{a: [1, 2, 3]}'), + ('model_backprop.py', 'comp', '{a: [1, 2, 3]}', False), ] @pytest.mark.parametrize( - 'filename, composition_name, input_dict_str', + 'filename, composition_name, input_dict_str, simple_edge_format', json_results_parametrization ) def test_json_results_equivalence( filename, composition_name, input_dict_str, + simple_edge_format, ): # Get python script from file and execute filename = f'{os.path.dirname(__file__)}/{filename}' @@ -61,9 +74,8 @@ def test_json_results_equivalence( # reset random seed pnl.core.globals.utilities.set_global_seed(0) - # Generate python script from JSON summary of composition and execute - json_summary = pnl.generate_json(eval(f'{composition_name}')) + json_summary = pnl.generate_json(eval(f'{composition_name}'), simple_edge_format=simple_edge_format) exec(pnl.generate_script_from_json(json_summary)) exec(f'{composition_name}.run(inputs={input_dict_str})') new_results = eval(f'{composition_name}.results') @@ -71,13 +83,14 @@ def test_json_results_equivalence( @pytest.mark.parametrize( - 'filename, composition_name, input_dict_str', + 'filename, composition_name, input_dict_str, simple_edge_format', json_results_parametrization ) def test_write_json_file( filename, composition_name, input_dict_str, + simple_edge_format, ): # Get python script from file and execute filename = f'{os.path.dirname(__file__)}/{filename}' @@ -91,7 +104,7 @@ def test_write_json_file( # Save json_summary of Composition to file and read back in. json_filename = filename.replace('.py','.json') - exec(f'pnl.write_json_file({composition_name}, json_filename)') + exec(f'pnl.write_json_file({composition_name}, json_filename, simple_edge_format=simple_edge_format)') exec(pnl.generate_script_from_json(json_filename)) # exec(f'{composition_name}.run(inputs={input_dict_str})') exec(f'pnl.get_compositions()[0].run(inputs={input_dict_str})') @@ -141,13 +154,15 @@ def test_write_json_file_multiple_comps( @pytest.mark.parametrize( - 'filename, composition_name, input_dict', + 'filename, composition_name, input_dict, simple_edge_format', [ - ('model_basic.py', 'comp', {'A': 1}), - ('model_udfs.py', 'comp', {'A': 10}) + ('model_basic.py', 'comp', {'A': [[1.0]]}, True), + ('model_basic.py', 'comp', {'A': 1}, False), + ('model_udfs.py', 'comp', {'A': [[10.0]]}, True), + ('model_udfs.py', 'comp', {'A': 10}, False), ] ) -def test_mdf_equivalence(filename, composition_name, input_dict): +def test_mdf_equivalence(filename, composition_name, input_dict, simple_edge_format): # Get python script from file and execute filename = f'{os.path.dirname(__file__)}/{filename}' with open(filename, 'r') as orig_file: @@ -158,7 +173,7 @@ def test_mdf_equivalence(filename, composition_name, input_dict): # Save json_summary of Composition to file and read back in. json_filename = filename.replace('.py', '.json') - pnl.write_json_file(eval(composition_name), json_filename) + pnl.write_json_file(eval(composition_name), json_filename, simple_edge_format=simple_edge_format) m = load_mdf(json_filename) eg = ee.EvaluableGraph(m.graphs[0], verbose=True) From 8e5017d8b2dee3ef8c0ab84274c11a9f0f78e5d7 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 9 Sep 2021 01:17:42 -0400 Subject: [PATCH 234/285] JSON: get rid of dummy nodes when importing to psyneulink --- .../core/components/projections/projection.py | 2 +- psyneulink/core/globals/json.py | 84 +++++++++++++++++-- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 2fb0e7421e6..d45da4ba6aa 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -1146,7 +1146,7 @@ def as_mdf_model(self, simple_edge_format=True): for name, value in parameters[self._model_spec_id_parameters].items(): if name not in {'weight'}: edge_node.parameters.append(mdf.Parameter(id=name, value=value)) - edge_node.metadata['orig_metadata'] = self._mdf_metadata[MODEL_SPEC_ID_METADATA] + edge_node.metadata.update(self._mdf_metadata[MODEL_SPEC_ID_METADATA]) post_edge = mdf.Edge( id=parse_valid_identifier(f'{self.name}_dummy_post_edge'), diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 2ea6f574911..bb7fdf0214a 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -202,8 +202,8 @@ from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ - MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE, MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, \ - MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_OUTPUT_PORTS, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE, MODEL_SPEC_ID_METADATA + MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE, MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, MODEL_SPEC_ID_RECEIVER_PORT, \ + MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_SENDER_PORT, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_OUTPUT_PORTS, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE, MODEL_SPEC_ID_METADATA from psyneulink.core.globals.parameters import ParameterAlias from psyneulink.core.globals.sampleiterator import SampleIterator from psyneulink.core.globals.utilities import convert_to_list, get_all_explicit_arguments, \ @@ -1067,7 +1067,7 @@ def alphabetical_order(items): for name, node in composition_dict[MODEL_SPEC_ID_NODES].items(): try: - _parse_component_type(node) + component_type = _parse_component_type(node) except KeyError: # will use a default type pass @@ -1080,6 +1080,77 @@ def alphabetical_order(items): if MODEL_SPEC_ID_COMPOSITION not in node: keys_to_delete.append(name) + else: + # projection was written out as a node for simple_edge_format + if issubclass(component_type, psyneulink.Projection_Base): + assert len(node[MODEL_SPEC_ID_INPUT_PORTS]) == 1 + assert len(node[MODEL_SPEC_ID_OUTPUT_PORTS]) == 1 + + extra_projs_to_delete = set() + + sender = None + sender_port = None + receiver = None + receiver_port = None + + for proj_name, proj in composition_dict[MODEL_SPEC_ID_PROJECTIONS].items(): + if proj[MODEL_SPEC_ID_RECEIVER_MECH] == name: + assert 'dummy' in proj_name + sender = proj[MODEL_SPEC_ID_SENDER_MECH] + sender_port = proj[MODEL_SPEC_ID_SENDER_PORT] + extra_projs_to_delete.add(proj_name) + + if proj[MODEL_SPEC_ID_SENDER_MECH] == name: + assert 'dummy' in proj_name + receiver = proj[MODEL_SPEC_ID_RECEIVER_MECH] + receiver_port = proj[MODEL_SPEC_ID_RECEIVER_PORT] + # if for some reason the projection has node as both sender and receiver + # this is a bug, let the deletion fail + extra_projs_to_delete.add(proj_name) + + if sender is None: + raise PNLJSONError(f'Dummy node {name} for projection has no sender in projections list') + + if receiver is None: + raise PNLJSONError(f'Dummy node {name} for projection has no receiver in projections list') + + proj_dict = { + **{ + MODEL_SPEC_ID_SENDER_PORT: sender_port, + MODEL_SPEC_ID_RECEIVER_PORT: receiver_port, + MODEL_SPEC_ID_SENDER_MECH: sender, + MODEL_SPEC_ID_RECEIVER_MECH: receiver + }, + **{ + MODEL_SPEC_ID_METADATA: { + # variable isn't specified for projections + **{k: v for k, v in node[MODEL_SPEC_ID_METADATA].items() if k != 'variable'}, + 'functions': node['functions'] + } + }, + } + try: + proj_dict[component_type._model_spec_id_parameters] = node[psyneulink.Component._model_spec_id_parameters] + except KeyError: + pass + + composition_dict[MODEL_SPEC_ID_PROJECTIONS][name.rstrip('_dummy_node')] = proj_dict + + keys_to_delete.append(name) + for p in extra_projs_to_delete: + del composition_dict[MODEL_SPEC_ID_PROJECTIONS][p] + + for nr_item in ['required_node_roles', 'excluded_node_roles']: + nr_removal_indices = [] + + for i, (nr_name, nr_role) in enumerate( + composition_dict[MODEL_SPEC_ID_METADATA][nr_item] + ): + if nr_name == name: + nr_removal_indices.append(i) + + for i in nr_removal_indices: + del composition_dict[MODEL_SPEC_ID_METADATA][nr_item][i] for nodes_dict in pnl_specific_items: for name, node in nodes_dict.items(): @@ -1279,9 +1350,10 @@ def alphabetical_order(items): if len(excluded_node_roles) > 0: for node, roles in excluded_node_roles.items(): - output.append( - f'{comp_identifer}.exclude_node_roles({node}, {_parse_parameter_value(roles, component_identifiers)})' - ) + if name not in implicit_names and name != controller_name: + output.append( + f'{comp_identifer}.exclude_node_roles({node}, {_parse_parameter_value(roles, component_identifiers)})' + ) output.append('') try: From 7bb39be5f467a22a18683965b17e13fdaf222bcd Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Mon, 25 Oct 2021 20:24:15 -0400 Subject: [PATCH 235/285] JSON: don't crash PNL if modeci_mdf not available modeci_mdf is not supported on 32-bit python because of onnxruntime, but some psyneulink functionality is tested and should still be able to run --- .github/actions/install-pnl/action.yml | 1 + psyneulink/core/components/component.py | 3 ++- .../core/components/functions/function.py | 5 +++-- .../functions/userdefinedfunction.py | 19 +++++++------------ .../core/components/mechanisms/mechanism.py | 3 ++- psyneulink/core/components/ports/inputport.py | 3 ++- .../core/components/ports/outputport.py | 4 ++-- .../core/components/projections/projection.py | 3 ++- psyneulink/core/compositions/composition.py | 3 ++- psyneulink/core/globals/json.py | 3 ++- psyneulink/core/scheduling/condition.py | 2 +- psyneulink/core/scheduling/scheduler.py | 3 ++- tests/json/test_json.py | 9 +++++++-- 13 files changed, 35 insertions(+), 26 deletions(-) diff --git a/.github/actions/install-pnl/action.yml b/.github/actions/install-pnl/action.yml index 0161d0e575d..8571a3293b0 100644 --- a/.github/actions/install-pnl/action.yml +++ b/.github/actions/install-pnl/action.yml @@ -42,6 +42,7 @@ runs: run: | if [ $(python -c 'import struct; print(struct.calcsize("P") * 8)') == 32 ]; then sed -i /torch/d requirements.txt + sed -i /modeci_mdf/d requirements.txt # pywinpty is a transitive dependency and v1.0+ removed support for x86 wheels # terminado >= 0.10.0 pulls in pywinpty >= 1.1.0 [[ ${{ runner.os }} = Windows* ]] && pip install "pywinpty<1" "terminado<0.10" diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 9a227008141..f92c2576655 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -508,7 +508,6 @@ import dill import graph_scheduler -import modeci_mdf.mdf as mdf import numpy as np from psyneulink.core import llvm as pnlvm @@ -3815,6 +3814,8 @@ def parse_parameter_value(value): } def _get_mdf_parameters(self): + import modeci_mdf.mdf as mdf + from psyneulink.core.compositions.composition import Composition from psyneulink.core.components.ports.port import Port from psyneulink.core.components.ports.outputport import OutputPort diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index 095c0840217..e86fcfc3a9b 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -146,8 +146,6 @@ import warnings from enum import Enum, IntEnum -import modeci_mdf.mdf as mdf -import modeci_mdf.functions.standard as mdf_functions import numpy as np import typecheck as tc @@ -832,6 +830,9 @@ def _dict_summary(self): } def as_mdf_model(self): + import modeci_mdf.mdf as mdf + import modeci_mdf.functions.standard as mdf_functions + if self._model_spec_generic_type_name is not NotImplemented: typ = self._model_spec_generic_type_name else: diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index 05a34779a29..8afdea8c069 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -720,22 +720,17 @@ def _dict_summary(self): def as_mdf_model(self): import math + import modeci_mdf.functions.standard model = super().as_mdf_model() ext_function_str = None - try: - import modeci_mdf.functions.standard - # remove import/module errors when modeci_mdf is a package - except (ImportError, ModuleNotFoundError): - pass - else: - if self.custom_function in [ - func_dict['function'] - for name, func_dict - in modeci_mdf.functions.standard.mdf_functions.items() - ]: - ext_function_str = self.custom_function.__name__ + if self.custom_function in [ + func_dict['function'] + for name, func_dict + in modeci_mdf.functions.standard.mdf_functions.items() + ]: + ext_function_str = self.custom_function.__name__ if _is_module_class(self.custom_function, math): ext_function_str = f'{self.custom_function.__module__}.{self.custom_function.__name__}' diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 42878a142bf..99927a45034 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1083,7 +1083,6 @@ from inspect import isclass from numbers import Number -import modeci_mdf.mdf as mdf import numpy as np import typecheck as tc @@ -4122,6 +4121,8 @@ def _dict_summary(self): } def as_mdf_model(self): + import modeci_mdf.mdf as mdf + model = mdf.Node( id=parse_valid_identifier(self.name), **self._mdf_metadata, diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 3efb064d15e..92af3edb118 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -565,7 +565,6 @@ import numbers import warnings -import modeci_mdf.mdf as mdf import numpy as np import typecheck as tc @@ -1468,6 +1467,8 @@ def _get_port_function_value(owner, function, variable): return Port_Base._get_port_function_value(owner=owner, function=function, variable=variable) def as_mdf_model(self): + import modeci_mdf.mdf as mdf + return mdf.InputPort( id=parse_valid_identifier(self.name), shape=str(self.defaults.variable.shape), diff --git a/psyneulink/core/components/ports/outputport.py b/psyneulink/core/components/ports/outputport.py index 9fbcf7b4ab4..3f8db2d41ad 100644 --- a/psyneulink/core/components/ports/outputport.py +++ b/psyneulink/core/components/ports/outputport.py @@ -621,8 +621,6 @@ import numpy as np import typecheck as tc -import modeci_mdf.mdf as mdf - from psyneulink.core.components.component import Component, ComponentError from psyneulink.core.components.functions.function import Function from psyneulink.core.components.ports.port import Port_Base, _instantiate_port_list, port_type_keywords @@ -1305,6 +1303,8 @@ def _dict_summary(self): } def as_mdf_model(self): + import modeci_mdf.mdf as mdf + owner_func_name = parse_valid_identifier(self.owner.function.name) if self._variable_spec == OWNER_VALUE: value = owner_func_name diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index d45da4ba6aa..4b4746a7f83 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -398,7 +398,6 @@ import warnings from collections import namedtuple, defaultdict -import modeci_mdf.mdf as mdf import numpy as np import typecheck as tc @@ -1080,6 +1079,8 @@ def _dict_summary(self): } def as_mdf_model(self, simple_edge_format=True): + import modeci_mdf.mdf as mdf + from psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism import CompositionInterfaceMechanism # these may occur during deferred init diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 27c99de08e3..e306798694b 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2706,7 +2706,6 @@ def input_function(env, result): from typing import Union import graph_scheduler -import modeci_mdf.mdf as mdf import networkx import numpy as np import pint @@ -11490,6 +11489,8 @@ def as_mdf_model(self, simple_edge_format=True): Returns: modeci_mdf.Model: a ModECI Model representing this Composition """ + import modeci_mdf.mdf as mdf + def is_included_projection(proj): included_types = ( CompositionInterfaceMechanism, diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index bb7fdf0214a..43051d5d22a 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -191,7 +191,6 @@ import inspect import json import math -import modeci_mdf.mdf as mdf import numbers import numpy import pickle @@ -239,6 +238,7 @@ class PNLJSONEncoder(json.JSONEncoder): into a more JSON-friendly format. """ def default(self, o): + import modeci_mdf.mdf as mdf from psyneulink.core.components.component import Component, ComponentsMeta if isinstance(o, ComponentsMeta): @@ -1591,6 +1591,7 @@ def generate_json(*compositions, simple_edge_format=True): in JSON """ import modeci_mdf + import modeci_mdf.mdf as mdf from psyneulink.core.compositions.composition import Composition model_name = "_".join([c.name for c in compositions]) diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index c5c46367445..62601cb4880 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -17,7 +17,6 @@ import dill import graph_scheduler -import modeci_mdf.mdf as mdf import numpy as np from psyneulink.core.globals.context import handle_external_context @@ -127,6 +126,7 @@ def _dict_summary(self): } def as_mdf_model(self): + import modeci_mdf.mdf as mdf from psyneulink.core.components.component import Component def _parse_condition_arg(arg): diff --git a/psyneulink/core/scheduling/scheduler.py b/psyneulink/core/scheduling/scheduler.py index 4138c090e40..65780f4b6f3 100644 --- a/psyneulink/core/scheduling/scheduler.py +++ b/psyneulink/core/scheduling/scheduler.py @@ -10,7 +10,6 @@ import typing import graph_scheduler -import modeci_mdf.mdf as mdf import pint from psyneulink import _unit_registry @@ -111,6 +110,8 @@ def _dict_summary(self): } def as_mdf_model(self): + import modeci_mdf.mdf as mdf + return mdf.ConditionSet( node_specific={ parse_valid_identifier(n.name): self.conditions[n].as_mdf_model() for n in self.nodes if n in self.conditions diff --git a/tests/json/test_json.py b/tests/json/test_json.py index d821bd4993d..51f5bbb8530 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -3,8 +3,10 @@ import psyneulink as pnl import pytest -from modeci_mdf.utils import load_mdf -import modeci_mdf.execution_engine as ee +pytest.importorskip( + 'modeci_mdf', + reason='JSON methods require modeci_mdf package' +) # stroop stimuli red = [1, 0] @@ -163,6 +165,9 @@ def test_write_json_file_multiple_comps( ] ) def test_mdf_equivalence(filename, composition_name, input_dict, simple_edge_format): + from modeci_mdf.utils import load_mdf + import modeci_mdf.execution_engine as ee + # Get python script from file and execute filename = f'{os.path.dirname(__file__)}/{filename}' with open(filename, 'r') as orig_file: From 2c59c406be0e078061ab30dc3afc07dae5479b47 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 10 Sep 2021 01:04:54 -0400 Subject: [PATCH 236/285] tests: add JSON model with non-identity matrix --- tests/json/model_basic_non_identity.py | 20 ++++++++++++++++++++ tests/json/test_json.py | 3 +++ 2 files changed, 23 insertions(+) create mode 100644 tests/json/model_basic_non_identity.py diff --git a/tests/json/model_basic_non_identity.py b/tests/json/model_basic_non_identity.py new file mode 100644 index 00000000000..2b76494c351 --- /dev/null +++ b/tests/json/model_basic_non_identity.py @@ -0,0 +1,20 @@ +import psyneulink as pnl + +comp = pnl.Composition(name='comp') +A = pnl.TransferMechanism(function=pnl.Linear(slope=5.0, intercept=2.0), name='A') +B = pnl.TransferMechanism(function=pnl.Logistic, name='B') + +for m in [A, B]: + comp.add_node(m) + +comp.add_projection(pnl.MappingProjection(matrix=[[2]]), A, B) + +comp.scheduler.add_condition_set({ + A: pnl.EveryNPasses(1), + B: pnl.EveryNCalls(A, 2), +}) + +comp.termination_processing = { + pnl.TimeScale.RUN: pnl.AfterNTrials(1), + pnl.TimeScale.TRIAL: pnl.AfterNCalls(B, 4) +} diff --git a/tests/json/test_json.py b/tests/json/test_json.py index 51f5bbb8530..b3a9c1ebbe6 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -25,6 +25,8 @@ json_results_parametrization = [ ('model_basic.py', 'comp', '{A: 1}', True), ('model_basic.py', 'comp', '{A: 1}', False), + ('model_basic_non_identity.py', 'comp', '{A: 1}', True), + ('model_basic_non_identity.py', 'comp', '{A: 1}', False), ('model_udfs.py', 'comp', '{A: 10}', True), ('model_udfs.py', 'comp', '{A: 10}', False), pytest.param( @@ -160,6 +162,7 @@ def test_write_json_file_multiple_comps( [ ('model_basic.py', 'comp', {'A': [[1.0]]}, True), ('model_basic.py', 'comp', {'A': 1}, False), + ('model_basic_non_identity.py', 'comp', {'A': [[1.0]]}, True), # requires simple edges ('model_udfs.py', 'comp', {'A': [[10.0]]}, True), ('model_udfs.py', 'comp', {'A': 10}, False), ] From 52b53a38f596393d9b367ea6330d250c95edd3f0 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 10 Sep 2021 02:10:58 -0400 Subject: [PATCH 237/285] JSON: fix using pnl-specific names for graph conditions --- psyneulink/core/globals/json.py | 16 ++++++++++++++-- psyneulink/core/scheduling/condition.py | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 43051d5d22a..f9ccfcb0cc3 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -188,6 +188,7 @@ import copy import dill import enum +import graph_scheduler import inspect import json import math @@ -921,8 +922,19 @@ def _parse_condition_arg_value(value): return str(_parse_parameter_value(value, component_identifiers)) + def _parse_graph_scheduler_type(typ): + for ts, pnl_ts in graph_scheduler.time._time_scale_aliases.items(): + ts_class_name = graph_scheduler.time._time_scale_to_class_str(ts) + pnl_ts_class_name = graph_scheduler.time._time_scale_to_class_str(pnl_ts) + + if ts_class_name in typ: + return typ.replace(ts_class_name, pnl_ts_class_name) + + return typ + args_str = '' - sig = inspect.signature(getattr(psyneulink, condition_dict[MODEL_SPEC_ID_TYPE]).__init__) + cond_type = _parse_graph_scheduler_type(condition_dict[MODEL_SPEC_ID_TYPE]) + sig = inspect.signature(getattr(psyneulink, cond_type).__init__) var_positional_arg_name = None @@ -971,7 +983,7 @@ def _parse_condition_arg_value(value): if len(arguments_str) > 0 and arguments_str[0] == ',': arguments_str = arguments_str[2:] - return f'psyneulink.{condition_dict[MODEL_SPEC_ID_TYPE]}({arguments_str})' + return f'psyneulink.{cond_type}({arguments_str})' def _generate_composition_string(graphs_dict, component_identifiers): diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index 62601cb4880..f7c23e08676 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -153,7 +153,7 @@ def _parse_condition_arg(arg): else: func_dict = {} - extra_args = {MODEL_SPEC_ID_TYPE: self.__class__.__name__} + extra_args = {MODEL_SPEC_ID_TYPE: getattr(graph_scheduler.condition, type(self).__name__).__name__} sig = inspect.signature(self.__init__) From c4cb169ebe9e474fa062a14cd14d97295018e5a5 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 14 Sep 2021 20:17:19 -0400 Subject: [PATCH 238/285] JSON: skip simplifying edges for identity projections --- psyneulink/core/components/projections/projection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 4b4746a7f83..0f4e56ccfb3 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -1115,7 +1115,7 @@ def as_mdf_model(self, simple_edge_format=True): if self.defaults.weight is None: parameters[self._model_spec_id_parameters]['weight'] = 1 - if simple_edge_format: + if simple_edge_format and not self.function._is_identity(defaults=True): edge_node = ProcessingMechanism( name=f'{self.name}_dummy_node', default_variable=self.defaults.variable, From 27e54cd3879ea5deae56714e13406b99fdddad7b Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 16 Sep 2021 20:53:50 -0400 Subject: [PATCH 239/285] JSON: throw warning for incompatible function names if functions share names with nodes or graphs, they'll be treated as references to the node or graph instead. --- psyneulink/core/globals/json.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index f9ccfcb0cc3..d996bbc8b97 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -199,6 +199,7 @@ import psyneulink import re import types +import warnings from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ @@ -206,7 +207,7 @@ MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_SENDER_PORT, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_OUTPUT_PORTS, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE, MODEL_SPEC_ID_METADATA from psyneulink.core.globals.parameters import ParameterAlias from psyneulink.core.globals.sampleiterator import SampleIterator -from psyneulink.core.globals.utilities import convert_to_list, get_all_explicit_arguments, \ +from psyneulink.core.globals.utilities import convert_to_list, gen_friendly_comma_str, get_all_explicit_arguments, \ parse_string_to_psyneulink_object_string, parse_valid_identifier, safe_equals, convert_to_np_array __all__ = [ @@ -651,6 +652,14 @@ def _generate_component_string( # pnl objects only have one function unless specified in another way # than just "function" if 'functions' in component_dict: + dup_function_names = set([name for name in component_dict['functions'] if name in component_identifiers]) + if len(dup_function_names) > 0: + warnings.warn( + f'Functions ({gen_friendly_comma_str(dup_function_names)}) of' + f' {name} share names of mechanisms or compositions in this' + ' model. This is likely to cause incorrect script reproduction.' + ) + function_determined_by_output_port = False try: From 4348b05aad148d24d43c3d6101d496be139e22d4 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 24 Sep 2021 23:49:50 -0400 Subject: [PATCH 240/285] JSON: handle multiple incoming projections to a port tests: JSON: add multiple incoming projections example --- .../core/components/mechanisms/mechanism.py | 55 +++++++++++++++++-- .../core/components/projections/projection.py | 12 +++- psyneulink/core/globals/json.py | 7 ++- psyneulink/core/globals/keywords.py | 2 + tests/json/model_varied_matrix_sizes.py | 15 +++++ tests/json/test_json.py | 3 + 6 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 tests/json/model_varied_matrix_sizes.py diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 99927a45034..767b8c73291 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1108,7 +1108,7 @@ MULTIPLICATIVE_PARAM, EXECUTION_COUNT, \ NAME, OUTPUT, OUTPUT_LABELS_DICT, OUTPUT_PORT, OUTPUT_PORT_PARAMS, OUTPUT_PORTS, OWNER_EXECUTION_COUNT, OWNER_VALUE, \ PARAMETER_PORT, PARAMETER_PORT_PARAMS, PARAMETER_PORTS, PROJECTIONS, REFERENCE_VALUE, RESULT, \ - TARGET_LABELS_DICT, VALUE, VARIABLE, WEIGHT + TARGET_LABELS_DICT, VALUE, VARIABLE, WEIGHT, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORT_COMBINATION_FUNCTION from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.registry import register_category, remove_instance_from_registry @@ -4132,10 +4132,48 @@ def as_mdf_model(self): model.parameters.append(mdf.Parameter(id=name, value=val)) for ip in self.input_ports: - ip_model = ip.as_mdf_model() - ip_model.id = f'{parse_valid_identifier(self.name)}_{ip_model.id}' + if len(ip.path_afferents) > 1: + for aff in ip.path_afferents: + ip_model = mdf.InputPort( + id=parse_valid_identifier(f'{self.name}_input_port_{aff.name}'), + shape=str(aff.defaults.value.shape), + type=str(aff.defaults.value.dtype) + ) + model.input_ports.append(ip_model) + + # create combination function + model.parameters.append( + mdf.Parameter( + id='combination_function_input_data', + value=f"[{', '.join(f'{mip.id}' for mip in model.input_ports)}]" + ) + ) + combination_function_id = f'{parse_valid_identifier(self.name)}_{MODEL_SPEC_ID_INPUT_PORT_COMBINATION_FUNCTION}' + model.functions.append( + mdf.Function( + id=combination_function_id, + function='onnx::ReduceSum', + args={ + 'data': "combination_function_input_data", + 'axes': 0 + } + ) + ) + combination_function_dimreduce_id = f'{combination_function_id}_dimreduce' + model.functions.append( + mdf.Function( + id=combination_function_dimreduce_id, + value=f'{MODEL_SPEC_ID_MDF_VARIABLE}[0][0]', + args={ + MODEL_SPEC_ID_MDF_VARIABLE: combination_function_id, + } + ) + ) + else: + ip_model = ip.as_mdf_model() + ip_model.id = f'{parse_valid_identifier(self.name)}_{ip_model.id}' - model.input_ports.append(ip_model) + model.input_ports.append(ip_model) for op in self.output_ports: op_model = op.as_mdf_model() @@ -4144,8 +4182,13 @@ def as_mdf_model(self): model.output_ports.append(op_model) function_model = self.function.as_mdf_model() - # primary input port - function_model.args[_get_variable_parameter_name(self.function)] = model.input_ports[0].id + + if len(ip.path_afferents) > 1: + primary_function_input_name = combination_function_dimreduce_id + else: + primary_function_input_name = model.input_ports[0].id + + function_model.args[_get_variable_parameter_name(self.function)] = primary_function_input_name model.functions.append(function_model) return model diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 0f4e56ccfb3..82218f6edfa 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -1095,7 +1095,17 @@ def as_mdf_model(self, simple_edge_format=True): sender_mech = None if not isinstance(self.receiver, type): - receiver_name = parse_valid_identifier(self.receiver.name) + try: + num_path_afferents = len(self.receiver.path_afferents) + except PortError: + # ParameterPort as receiver + num_path_afferents = 0 + + if num_path_afferents > 1: + receiver_name = parse_valid_identifier(f'input_port_{self.name}') + else: + receiver_name = parse_valid_identifier(self.receiver.name) + if isinstance(self.receiver.owner, CompositionInterfaceMechanism): receiver_mech = parse_valid_identifier(self.receiver.owner.composition.name) else: diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index d996bbc8b97..37abdcfb028 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -204,7 +204,7 @@ from psyneulink.core.globals.keywords import \ MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PARAMETER_SOURCE, \ MODEL_SPEC_ID_PARAMETER_INITIAL_VALUE, MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_RECEIVER_MECH, MODEL_SPEC_ID_RECEIVER_PORT, \ - MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_SENDER_PORT, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_OUTPUT_PORTS, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE, MODEL_SPEC_ID_METADATA + MODEL_SPEC_ID_SENDER_MECH, MODEL_SPEC_ID_SENDER_PORT, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_OUTPUT_PORTS, MODEL_SPEC_ID_MDF_VARIABLE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_SHAPE, MODEL_SPEC_ID_METADATA, MODEL_SPEC_ID_INPUT_PORT_COMBINATION_FUNCTION from psyneulink.core.globals.parameters import ParameterAlias from psyneulink.core.globals.sampleiterator import SampleIterator from psyneulink.core.globals.utilities import convert_to_list, gen_friendly_comma_str, get_all_explicit_arguments, \ @@ -687,7 +687,10 @@ def _generate_component_string( if function_determined_by_output_port and MODEL_SPEC_ID_PARAMETER_VALUE in primary_output_port: parameter_names['function'] = re.sub(r'(.*)\[\d+\]', '\\1', primary_output_port[MODEL_SPEC_ID_PARAMETER_VALUE]) else: - parameter_names['function'] = list(component_dict['functions'])[0] + parameter_names['function'] = [ + f for f in component_dict['functions'] + if not f.endswith(MODEL_SPEC_ID_INPUT_PORT_COMBINATION_FUNCTION) + ][0] parameters['function'] = { parameter_names['function']: component_dict['functions'][parameter_names['function']] diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index 281ad2bfed7..c65e6a5c965 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -1004,3 +1004,5 @@ def _is_metric(metric): MODEL_SPEC_ID_MDF_VARIABLE = 'variable0' MODEL_SPEC_ID_SHAPE = 'shape' + +MODEL_SPEC_ID_INPUT_PORT_COMBINATION_FUNCTION = 'input_combination_function' diff --git a/tests/json/model_varied_matrix_sizes.py b/tests/json/model_varied_matrix_sizes.py new file mode 100644 index 00000000000..900f0b570f1 --- /dev/null +++ b/tests/json/model_varied_matrix_sizes.py @@ -0,0 +1,15 @@ +import psyneulink as pnl + +comp = pnl.Composition(name='comp') +A = pnl.TransferMechanism(name='A', size=2) +B = pnl.TransferMechanism(name='B', size=3) +C = pnl.TransferMechanism(name='C', size=4) +D = pnl.TransferMechanism(name='D', size=5) + +for n in [A, B, C, D]: + comp.add_node(n) + +comp.add_projection(pnl.MappingProjection(matrix=[[1, 2, 3], [4, 5, 6]]), A, B) +comp.add_projection(pnl.MappingProjection(matrix=[[1, 2, 3, 4], [5, 6, 7, 8]]), A, C) +comp.add_projection(pnl.MappingProjection(matrix=[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]), B, D) +comp.add_projection(pnl.MappingProjection(matrix=[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]), C, D) diff --git a/tests/json/test_json.py b/tests/json/test_json.py index b3a9c1ebbe6..e98ee61b267 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -29,6 +29,8 @@ ('model_basic_non_identity.py', 'comp', '{A: 1}', False), ('model_udfs.py', 'comp', '{A: 10}', True), ('model_udfs.py', 'comp', '{A: 10}', False), + ('model_varied_matrix_sizes.py', 'comp', '{A: [1, 2]}', True), + ('model_varied_matrix_sizes.py', 'comp', '{A: [1, 2]}', False), pytest.param( 'model_nested_comp_with_scheduler.py', 'comp', @@ -165,6 +167,7 @@ def test_write_json_file_multiple_comps( ('model_basic_non_identity.py', 'comp', {'A': [[1.0]]}, True), # requires simple edges ('model_udfs.py', 'comp', {'A': [[10.0]]}, True), ('model_udfs.py', 'comp', {'A': 10}, False), + ('model_varied_matrix_sizes.py', 'comp', {'A': [[1.0, 2.0]]}, True), ] ) def test_mdf_equivalence(filename, composition_name, input_dict, simple_edge_format): From c2d5fd43ef3f99ea1ebddb7f463ee1066266298c Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 5 Oct 2021 01:46:57 -0400 Subject: [PATCH 241/285] JSON: support integrator functions as expressions JSON: functions: add initializers as stateful parameters tests: add integrator tests --- .../core/components/functions/function.py | 63 +++++++++++++++---- .../functions/stateful/integratorfunctions.py | 15 ++++- .../core/components/mechanisms/mechanism.py | 3 + .../processing/integratormechanism.py | 16 +++++ .../processing/transfermechanism.py | 25 +++++++- tests/json/model_integrators.py | 52 +++++++++++++++ tests/json/test_json.py | 4 ++ 7 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 tests/json/model_integrators.py diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index e86fcfc3a9b..fbd977381a6 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -156,7 +156,7 @@ ARGUMENT_THERAPY_FUNCTION, AUTO_ASSIGN_MATRIX, EXAMPLE_FUNCTION_TYPE, FULL_CONNECTIVITY_MATRIX, FUNCTION_COMPONENT_CATEGORY, FUNCTION_OUTPUT_TYPE, FUNCTION_OUTPUT_TYPE_CONVERSION, HOLLOW_MATRIX, IDENTITY_MATRIX, INVERSE_HOLLOW_MATRIX, NAME, PREFERENCE_SET_NAME, RANDOM_CONNECTIVITY_MATRIX, VALUE, VARIABLE, - MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC + MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_METADATA ) from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import REPORT_OUTPUT_PREF, is_pref_set @@ -833,24 +833,61 @@ def as_mdf_model(self): import modeci_mdf.mdf as mdf import modeci_mdf.functions.standard as mdf_functions - if self._model_spec_generic_type_name is not NotImplemented: - typ = self._model_spec_generic_type_name - else: + parameters = self._mdf_model_parameters + metadata = self._mdf_metadata + metadata[MODEL_SPEC_ID_METADATA]['function_stateful_params'] = {} + stateful_params = set() + + # add stateful parameters into metadata for mechanism to get + for name in parameters[self._model_spec_id_parameters]: try: - typ = self.custom_function.__name__ + param = getattr(self.parameters, name) except AttributeError: - typ = type(self).__name__.lower() - - if typ not in mdf_functions.mdf_functions: - warnings.warn(f'{typ} is not an MDF standard function, this is likely to produce an incompatible model.') + continue - return mdf.Function( + if param.initializer is not None: + # in this case, parameter gets updated to its function's final value + try: + initializer_value = parameters[self._model_spec_id_parameters][param.initializer] + except KeyError: + initializer_value = metadata[MODEL_SPEC_ID_METADATA]['initializer'] + + metadata[MODEL_SPEC_ID_METADATA]['function_stateful_params'][name] = { + 'id': name, + 'default_initial_value': initializer_value, + 'value': parse_valid_identifier(self.name) + } + stateful_params.add(name) + + # stateful parameters cannot show up as args or they will not be + # treated statefully in mdf + for sp in stateful_params: + del parameters[self._model_spec_id_parameters][sp] + + model = mdf.Function( id=parse_valid_identifier(self.name), - function=typ, - **self._mdf_model_parameters, - **self._mdf_metadata + **parameters, + **metadata, ) + try: + model.value = self.as_expression() + except AttributeError: + if self._model_spec_generic_type_name is not NotImplemented: + typ = self._model_spec_generic_type_name + else: + try: + typ = self.custom_function.__name__ + except AttributeError: + typ = type(self).__name__.lower() + + if typ not in mdf_functions.mdf_functions: + warnings.warn(f'{typ} is not an MDF standard function, this is likely to produce an incompatible model.') + + model.function = typ + + return model + # ***************************************** EXAMPLE FUNCTION ******************************************************* PROPENSITY = "PROPENSITY" diff --git a/psyneulink/core/components/functions/stateful/integratorfunctions.py b/psyneulink/core/components/functions/stateful/integratorfunctions.py index 1cc57f1d775..cf99ccd0048 100644 --- a/psyneulink/core/components/functions/stateful/integratorfunctions.py +++ b/psyneulink/core/components/functions/stateful/integratorfunctions.py @@ -47,7 +47,7 @@ INCREMENT, INITIALIZER, INPUT_PORTS, INTEGRATOR_FUNCTION, INTEGRATOR_FUNCTION_TYPE, \ INTERACTIVE_ACTIVATION_INTEGRATOR_FUNCTION, LEAKY_COMPETING_INTEGRATOR_FUNCTION, \ MULTIPLICATIVE_PARAM, NOISE, OFFSET, OPERATION, ORNSTEIN_UHLENBECK_INTEGRATOR_FUNCTION, OUTPUT_PORTS, PRODUCT, \ - RATE, REST, SIMPLE_INTEGRATOR_FUNCTION, SUM, TIME_STEP_SIZE, THRESHOLD, VARIABLE + RATE, REST, SIMPLE_INTEGRATOR_FUNCTION, SUM, TIME_STEP_SIZE, THRESHOLD, VARIABLE, MODEL_SPEC_ID_MDF_VARIABLE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.utilities import parameter_spec, all_within_range, \ @@ -689,6 +689,9 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): builder.store(res, vo_ptr) builder.store(res, prev_ptr) + def as_expression(self): + return 'previous_value * rate + increment' + class SimpleIntegrator(IntegratorFunction): # ------------------------------------------------------------------------- """ @@ -917,6 +920,10 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): builder.store(res, vo_ptr) builder.store(res, prev_ptr) + def as_expression(self): + return f'previous_value + ({MODEL_SPEC_ID_MDF_VARIABLE} * rate) + offset' + + class AdaptiveIntegrator(IntegratorFunction): # ----------------------------------------------------------------------- """ AdaptiveIntegrator( \ @@ -1236,6 +1243,9 @@ def _function(self, return self.convert_output_type(adjusted_value, variable) # MODIFIED 6/21/19 END + def as_expression(self): + return f'(1 - rate) * previous_value + rate * {MODEL_SPEC_ID_MDF_VARIABLE} + offset' + S_MINUS_L = 's-l' L_MINUS_S = 'l-s' @@ -3836,6 +3846,9 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): builder.store(ret, out_ptr) builder.store(ret, prev_ptr) + def as_expression(self): + return f'previous_value + (-rate * previous_value + {MODEL_SPEC_ID_MDF_VARIABLE}) * time_step_size' + class FitzHughNagumoIntegrator(IntegratorFunction): # ---------------------------------------------------------------------------- """ diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 767b8c73291..aab9893204c 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -4183,6 +4183,9 @@ def as_mdf_model(self): function_model = self.function.as_mdf_model() + for _, func_param in function_model.metadata['function_stateful_params'].items(): + model.parameters.append(mdf.Parameter(**func_param)) + if len(ip.path_afferents) > 1: primary_function_input_name = combination_function_dimreduce_id else: diff --git a/psyneulink/core/components/mechanisms/processing/integratormechanism.py b/psyneulink/core/components/mechanisms/processing/integratormechanism.py index 9b479cc0b71..53736e23994 100644 --- a/psyneulink/core/components/mechanisms/processing/integratormechanism.py +++ b/psyneulink/core/components/mechanisms/processing/integratormechanism.py @@ -94,6 +94,7 @@ from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set, REPORT_OUTPUT_PREF from psyneulink.core.globals.preferences.preferenceset import PreferenceEntry, PreferenceLevel +from psyneulink.core.globals.utilities import parse_valid_identifier __all__ = [ 'DEFAULT_RATE', 'IntegratorMechanism', 'IntegratorMechanismError' @@ -228,3 +229,18 @@ def _handle_default_variable(self, default_variable=None, size=None, input_ports input_ports=input_ports, function=function, params=params) + + def as_mdf_model(self): + import modeci_mdf.mdf as mdf + + model = super().as_mdf_model() + function_model = [ + f for f in model.functions + if f.id == parse_valid_identifier(self.function.name) + ][0] + assert function_model.id == parse_valid_identifier(self.function.name), (function_model.id, parse_valid_identifier(self.function.name)) + + for _, func_param in function_model.metadata['function_stateful_params'].items(): + model.parameters.append(mdf.Parameter(**func_param)) + + return model diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index 5a5563a0816..74fd1ac3866 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -842,6 +842,7 @@ from psyneulink.core.components.ports.inputport import InputPort from psyneulink.core.components.ports.outputport import OutputPort from psyneulink.core.globals.context import ContextFlags, handle_external_context +from psyneulink.core.globals.json import _get_variable_parameter_name from psyneulink.core.globals.keywords import \ COMBINE, comparison_operators, EXECUTION_COUNT, FUNCTION, GREATER_THAN_OR_EQUAL, \ CURRENT_VALUE, LESS_THAN_OR_EQUAL, MAX_ABS_DIFF, \ @@ -851,7 +852,7 @@ from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.utilities import \ - all_within_range, append_type_to_name, iscompatible, is_comparison_operator, convert_to_np_array, safe_equals + all_within_range, append_type_to_name, iscompatible, is_comparison_operator, convert_to_np_array, safe_equals, parse_valid_identifier from psyneulink.core.scheduling.time import TimeScale __all__ = [ @@ -1806,3 +1807,25 @@ def _update_default_variable(self, new_default_variable, context=None): self.parameters.initial_value._set(copy.deepcopy(integrator_function_variable), context) super()._update_default_variable(new_default_variable, context=context) + + def as_mdf_model(self): + import modeci_mdf.mdf as mdf + + model = super().as_mdf_model() + function_model = [ + f for f in model.functions + if f.id == parse_valid_identifier(self.function.name) + ][0] + assert function_model.id == parse_valid_identifier(self.function.name), (function_model.id, parse_valid_identifier(self.function.name)) + + if self.defaults.integrator_mode: + integrator_function_model = self.integrator_function.as_mdf_model() + primary_input = function_model.args[_get_variable_parameter_name(self.function)] + integrator_function_model.args[_get_variable_parameter_name(self.function)] = primary_input + function_model.args[_get_variable_parameter_name(self.function)] = integrator_function_model.id + + for _, func_param in integrator_function_model.metadata['function_stateful_params'].items(): + model.parameters.append(mdf.Parameter(**func_param)) + + model.functions.append(integrator_function_model) + return model diff --git a/tests/json/model_integrators.py b/tests/json/model_integrators.py new file mode 100644 index 00000000000..cfcb563f70d --- /dev/null +++ b/tests/json/model_integrators.py @@ -0,0 +1,52 @@ +import psyneulink as pnl + +comp = pnl.Composition(name="comp") +A = pnl.TransferMechanism( + name="A", + function=pnl.Linear(slope=0.5, intercept=1.0), + integrator_mode=True, + integrator_function=pnl.SimpleIntegrator(rate=0.5, offset=1), +) +B = pnl.TransferMechanism( + name="B", + function=pnl.Logistic(gain=0.1), + integrator_mode=True, + integration_rate=0.9, + integrator_function=pnl.AdaptiveIntegrator(offset=-1), +) + +C = pnl.TransferMechanism( + name="C", + integrator_mode=True, + integration_rate=0.5, + integrator_function=pnl.AccumulatorIntegrator, +) + +D = pnl.TransferMechanism( + name="D", + integrator_mode=True, + integration_rate=0.5, + integrator_function=pnl.LeakyCompetingIntegrator(time_step_size=0.2), +) + +E = pnl.IntegratorMechanism( + name="E", + function=pnl.SimpleIntegrator(rate=0.5, offset=-1) +) + +comp.add_linear_processing_pathway([A, B, C, D, E]) + +comp.scheduler.add_condition_set( + { + A: pnl.EveryNPasses(1), + B: pnl.EveryNCalls(A, 2), + C: pnl.EveryNCalls(B, 2), + D: pnl.EveryNCalls(C, 2), + E: pnl.EveryNCalls(D, 2), + } +) + +comp.termination_processing = { + pnl.TimeScale.RUN: pnl.AfterNTrials(1), + pnl.TimeScale.TRIAL: pnl.All(pnl.Not(pnl.BeforeNCalls(E, 5))), +} diff --git a/tests/json/test_json.py b/tests/json/test_json.py index e98ee61b267..fff5046abe2 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -31,6 +31,8 @@ ('model_udfs.py', 'comp', '{A: 10}', False), ('model_varied_matrix_sizes.py', 'comp', '{A: [1, 2]}', True), ('model_varied_matrix_sizes.py', 'comp', '{A: [1, 2]}', False), + ('model_integrators.py', 'comp', '{A: 1.0}', True), + ('model_integrators.py', 'comp', '{A: 1.0}', False), pytest.param( 'model_nested_comp_with_scheduler.py', 'comp', @@ -168,6 +170,8 @@ def test_write_json_file_multiple_comps( ('model_udfs.py', 'comp', {'A': [[10.0]]}, True), ('model_udfs.py', 'comp', {'A': 10}, False), ('model_varied_matrix_sizes.py', 'comp', {'A': [[1.0, 2.0]]}, True), + ('model_integrators.py', 'comp', {'A': [[1.0]]}, True), + ('model_integrators.py', 'comp', {'A': 1.0}, False), ] ) def test_mdf_equivalence(filename, composition_name, input_dict, simple_edge_format): From bfeb8314b13680ada0c5aed16dd19dad4a3eebd5 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 7 Oct 2021 01:17:54 -0400 Subject: [PATCH 242/285] JSON: TransferMechanism: support noise --- .../core/components/functions/function.py | 35 +++++++++-- .../nonstateful/distributionfunctions.py | 9 ++- .../functions/stateful/integratorfunctions.py | 8 +-- .../processing/integratormechanism.py | 11 ++++ .../processing/transfermechanism.py | 12 ++++ tests/json/model_integrators.py | 25 ++++++-- tests/json/test_json.py | 62 +++++++++++++++---- 7 files changed, 137 insertions(+), 25 deletions(-) diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index fbd977381a6..ed07a0c8fa1 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -141,6 +141,7 @@ """ import abc +import inspect import numbers import types import warnings @@ -149,22 +150,22 @@ import numpy as np import typecheck as tc -from psyneulink.core.components.component import ComponentError, DefaultsFlexibility +from psyneulink.core.components.component import Component, ComponentError, DefaultsFlexibility from psyneulink.core.components.shellclasses import Function, Mechanism from psyneulink.core.globals.context import ContextFlags, handle_external_context from psyneulink.core.globals.keywords import ( ARGUMENT_THERAPY_FUNCTION, AUTO_ASSIGN_MATRIX, EXAMPLE_FUNCTION_TYPE, FULL_CONNECTIVITY_MATRIX, FUNCTION_COMPONENT_CATEGORY, FUNCTION_OUTPUT_TYPE, FUNCTION_OUTPUT_TYPE_CONVERSION, HOLLOW_MATRIX, IDENTITY_MATRIX, INVERSE_HOLLOW_MATRIX, NAME, PREFERENCE_SET_NAME, RANDOM_CONNECTIVITY_MATRIX, VALUE, VARIABLE, - MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_METADATA + MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_METADATA, MODEL_SPEC_ID_MDF_VARIABLE ) from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import REPORT_OUTPUT_PREF, is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceEntry, PreferenceLevel from psyneulink.core.globals.registry import register_category from psyneulink.core.globals.utilities import ( - convert_to_np_array, get_global_seed, object_has_single_value, parameter_spec, parse_valid_identifier, safe_len, - SeededRandomState, contains_type, is_instance_or_subclass + convert_to_np_array, get_global_seed, is_instance_or_subclass, object_has_single_value, parameter_spec, parse_valid_identifier, safe_len, + SeededRandomState, contains_type ) __all__ = [ @@ -829,6 +830,32 @@ def _dict_summary(self): 'function': type_str.lower() } + def _get_mdf_noise_function(self): + import modeci_mdf.mdf as mdf + + extra_noise_functions = [] + + def handle_noise(noise): + if is_instance_or_subclass(noise, Component): + if inspect.isclass(noise) and issubclass(noise, Component): + noise = noise() + noise_func_model = noise.as_mdf_model() + extra_noise_functions.append(noise_func_model) + return noise_func_model.id + elif isinstance(noise, (list, np.ndarray)): + return type(noise)(handle_noise(item) for item in noise) + else: + return None + + noise = handle_noise(self.defaults.noise) + + if noise is not None: + return mdf.Function( + id=f'{parse_valid_identifier(self.name)}_noise', + value=MODEL_SPEC_ID_MDF_VARIABLE, + args={MODEL_SPEC_ID_MDF_VARIABLE: noise}, + ), extra_noise_functions + def as_mdf_model(self): import modeci_mdf.mdf as mdf import modeci_mdf.functions.standard as mdf_functions diff --git a/psyneulink/core/components/functions/nonstateful/distributionfunctions.py b/psyneulink/core/components/functions/nonstateful/distributionfunctions.py index 80c979fcb50..565fdaa112a 100644 --- a/psyneulink/core/components/functions/nonstateful/distributionfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/distributionfunctions.py @@ -51,6 +51,11 @@ class DistributionFunction(Function_Base): componentType = DIST_FUNCTION_TYPE + def as_mdf_model(self): + model = super().as_mdf_model() + model.args['shape'] = self.defaults.variable.shape + return model + class NormalDist(DistributionFunction): """ @@ -124,6 +129,7 @@ class NormalDist(DistributionFunction): """ componentName = NORMAL_DIST_FUNCTION + _model_spec_generic_type_name = 'onnx::RandomNormal' class Parameters(DistributionFunction.Parameters): """ @@ -149,7 +155,7 @@ class Parameters(DistributionFunction.Parameters): :type: ``numpy.random.RandomState`` """ mean = Parameter(0.0, modulable=True, aliases=[ADDITIVE_PARAM]) - standard_deviation = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM]) + standard_deviation = Parameter(1.0, modulable=True, aliases=[MULTIPLICATIVE_PARAM], mdf_name='scale') random_state = Parameter(None, loggable=False, getter=_random_state_getter, dependencies='seed') seed = Parameter(DEFAULT_SEED, modulable=True, fallback_default=True, setter=_seed_setter) @@ -557,6 +563,7 @@ class UniformDist(DistributionFunction): """ componentName = UNIFORM_DIST_FUNCTION + _model_spec_generic_type_name = 'onnx::RandomUniform' class Parameters(DistributionFunction.Parameters): """ diff --git a/psyneulink/core/components/functions/stateful/integratorfunctions.py b/psyneulink/core/components/functions/stateful/integratorfunctions.py index cf99ccd0048..65dd4b596b8 100644 --- a/psyneulink/core/components/functions/stateful/integratorfunctions.py +++ b/psyneulink/core/components/functions/stateful/integratorfunctions.py @@ -690,7 +690,7 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): builder.store(res, prev_ptr) def as_expression(self): - return 'previous_value * rate + increment' + return 'previous_value * rate + noise + increment' class SimpleIntegrator(IntegratorFunction): # ------------------------------------------------------------------------- @@ -921,7 +921,7 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): builder.store(res, prev_ptr) def as_expression(self): - return f'previous_value + ({MODEL_SPEC_ID_MDF_VARIABLE} * rate) + offset' + return f'previous_value + ({MODEL_SPEC_ID_MDF_VARIABLE} * rate) + noise + offset' class AdaptiveIntegrator(IntegratorFunction): # ----------------------------------------------------------------------- @@ -1244,7 +1244,7 @@ def _function(self, # MODIFIED 6/21/19 END def as_expression(self): - return f'(1 - rate) * previous_value + rate * {MODEL_SPEC_ID_MDF_VARIABLE} + offset' + return f'(1 - rate) * previous_value + rate * {MODEL_SPEC_ID_MDF_VARIABLE} + noise + offset' S_MINUS_L = 's-l' @@ -3847,7 +3847,7 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): builder.store(ret, prev_ptr) def as_expression(self): - return f'previous_value + (-rate * previous_value + {MODEL_SPEC_ID_MDF_VARIABLE}) * time_step_size' + return f'previous_value + (-rate * previous_value + {MODEL_SPEC_ID_MDF_VARIABLE}) * time_step_size + noise * (time_step_size ** 0.5)' class FitzHughNagumoIntegrator(IntegratorFunction): # ---------------------------------------------------------------------------- diff --git a/psyneulink/core/components/mechanisms/processing/integratormechanism.py b/psyneulink/core/components/mechanisms/processing/integratormechanism.py index 53736e23994..5ab4fa8cf01 100644 --- a/psyneulink/core/components/mechanisms/processing/integratormechanism.py +++ b/psyneulink/core/components/mechanisms/processing/integratormechanism.py @@ -243,4 +243,15 @@ def as_mdf_model(self): for _, func_param in function_model.metadata['function_stateful_params'].items(): model.parameters.append(mdf.Parameter(**func_param)) + res = self.function._get_mdf_noise_function() + try: + main_noise_function, extra_noise_functions = res + except TypeError: + pass + else: + main_noise_function.id = f'{model.id}_{main_noise_function.id}' + model.functions.append(main_noise_function) + model.functions.extend(extra_noise_functions) + function_model.args['noise'] = main_noise_function.id + return model diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index 74fd1ac3866..eaad3d4bd7b 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -1828,4 +1828,16 @@ def as_mdf_model(self): model.parameters.append(mdf.Parameter(**func_param)) model.functions.append(integrator_function_model) + + res = self.integrator_function._get_mdf_noise_function() + try: + main_noise_function, extra_noise_functions = res + except TypeError: + pass + else: + main_noise_function.id = f'{model.id}_{main_noise_function.id}' + model.functions.append(main_noise_function) + model.functions.extend(extra_noise_functions) + integrator_function_model.args['noise'] = main_noise_function.id + return model diff --git a/tests/json/model_integrators.py b/tests/json/model_integrators.py index cfcb563f70d..100b7985319 100644 --- a/tests/json/model_integrators.py +++ b/tests/json/model_integrators.py @@ -5,33 +5,48 @@ name="A", function=pnl.Linear(slope=0.5, intercept=1.0), integrator_mode=True, - integrator_function=pnl.SimpleIntegrator(rate=0.5, offset=1), + integrator_function=pnl.SimpleIntegrator( + rate=0.5, + noise=pnl.UniformDist(low=-1.0, high=1.0, seed=0) + ), ) B = pnl.TransferMechanism( name="B", function=pnl.Logistic(gain=0.1), integrator_mode=True, integration_rate=0.9, - integrator_function=pnl.AdaptiveIntegrator(offset=-1), + integrator_function=pnl.AdaptiveIntegrator( + offset=-1, + noise=pnl.NormalDist(mean=-1.0, standard_deviation=0.5, seed=0) + ), ) C = pnl.TransferMechanism( name="C", integrator_mode=True, integration_rate=0.5, - integrator_function=pnl.AccumulatorIntegrator, + integrator_function=pnl.AccumulatorIntegrator( + noise=pnl.NormalDist(standard_deviation=0.25, seed=0) + ), ) D = pnl.TransferMechanism( name="D", integrator_mode=True, integration_rate=0.5, - integrator_function=pnl.LeakyCompetingIntegrator(time_step_size=0.2), + integrator_function=pnl.LeakyCompetingIntegrator( + noise=pnl.UniformDist(low=-0.5, high=0.5, seed=0), + time_step_size=0.2, + ), ) E = pnl.IntegratorMechanism( name="E", - function=pnl.SimpleIntegrator(rate=0.5, offset=-1) + function=pnl.SimpleIntegrator( + rate=0.5, + offset=-1, + noise=pnl.UniformDist(low=-0.25, high=0.5, seed=0) + ) ) comp.add_linear_processing_pathway([A, B, C, D, E]) diff --git a/tests/json/test_json.py b/tests/json/test_json.py index fff5046abe2..091e55def88 100644 --- a/tests/json/test_json.py +++ b/tests/json/test_json.py @@ -2,6 +2,8 @@ import os import psyneulink as pnl import pytest +import sys + pytest.importorskip( 'modeci_mdf', @@ -161,20 +163,58 @@ def test_write_json_file_multiple_comps( assert orig_results[composition_name] == final_results, f'{composition_name}:' +# These runtime_params are necessary because noise seeding is not +# replicable between numpy and onnx. +# Values are generated from running onnx function RandomUniform and +# RandomNormal with parameters used in model_integrators.py (seed 0). +# RandomNormal values are different on mac versus linux and windows +if sys.platform == 'linux': + onnx_integrators_fixed_seeded_noise = { + 'A': [[-0.9999843239784241]], + 'B': [[-1.1295466423034668]], + 'C': [[-0.0647732987999916]], + 'D': [[-0.499992161989212]], + 'E': [[-0.2499941289424896]], + } +elif sys.platform == 'win32': + onnx_integrators_fixed_seeded_noise = { + 'A': [[0.0976270437240601]], + 'B': [[-0.4184607267379761]], + 'C': [[0.290769636631012]], + 'D': [[0.04881352186203]], + 'E': [[0.1616101264953613]], + } +else: + assert sys.platform == 'darwin' + onnx_integrators_fixed_seeded_noise = { + 'A': [[-0.9999550580978394]], + 'B': [[-0.8846577405929565]], + 'C': [[0.0576711297035217]], + 'D': [[-0.4999775290489197]], + 'E': [[-0.2499831467866898]], + } + +integrators_runtime_params = ( + 'runtime_params={' + + ','.join([f'{k}: {{ "noise": {v} }}' for k, v in onnx_integrators_fixed_seeded_noise.items()]) + + '}' +) + + @pytest.mark.parametrize( - 'filename, composition_name, input_dict, simple_edge_format', + 'filename, composition_name, input_dict, simple_edge_format, run_args', [ - ('model_basic.py', 'comp', {'A': [[1.0]]}, True), - ('model_basic.py', 'comp', {'A': 1}, False), - ('model_basic_non_identity.py', 'comp', {'A': [[1.0]]}, True), # requires simple edges - ('model_udfs.py', 'comp', {'A': [[10.0]]}, True), - ('model_udfs.py', 'comp', {'A': 10}, False), - ('model_varied_matrix_sizes.py', 'comp', {'A': [[1.0, 2.0]]}, True), - ('model_integrators.py', 'comp', {'A': [[1.0]]}, True), - ('model_integrators.py', 'comp', {'A': 1.0}, False), + ('model_basic.py', 'comp', {'A': [[1.0]]}, True, ''), + ('model_basic.py', 'comp', {'A': 1}, False, ''), + ('model_basic_non_identity.py', 'comp', {'A': [[1.0]]}, True, ''), # requires simple edges + ('model_udfs.py', 'comp', {'A': [[10.0]]}, True, ''), + ('model_udfs.py', 'comp', {'A': 10}, False, ''), + ('model_varied_matrix_sizes.py', 'comp', {'A': [[1.0, 2.0]]}, True, ''), + ('model_integrators.py', 'comp', {'A': 1.0}, True, integrators_runtime_params), + ('model_integrators.py', 'comp', {'A': 1.0}, False, integrators_runtime_params), ] ) -def test_mdf_equivalence(filename, composition_name, input_dict, simple_edge_format): +def test_mdf_equivalence(filename, composition_name, input_dict, simple_edge_format, run_args): from modeci_mdf.utils import load_mdf import modeci_mdf.execution_engine as ee @@ -183,7 +223,7 @@ def test_mdf_equivalence(filename, composition_name, input_dict, simple_edge_for with open(filename, 'r') as orig_file: exec(orig_file.read()) inputs_str = str(input_dict).replace("'", '') - exec(f'{composition_name}.run(inputs={inputs_str})') + exec(f'{composition_name}.run(inputs={inputs_str}, {run_args})') orig_results = eval(f'{composition_name}.results') # Save json_summary of Composition to file and read back in. From dc773a025e393ec93eabdb0b9872cf79425d4a16 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 25 Feb 2022 22:23:41 -0500 Subject: [PATCH 243/285] JSON: remove _dict_summary (replaced by mdf methods) --- psyneulink/core/components/component.py | 168 +----------------- .../core/components/functions/function.py | 16 +- .../functions/userdefinedfunction.py | 31 +--- .../core/components/mechanisms/mechanism.py | 25 +-- .../core/components/ports/outputport.py | 10 -- psyneulink/core/components/ports/port.py | 11 -- .../core/components/projections/projection.py | 37 ---- psyneulink/core/compositions/composition.py | 63 +------ psyneulink/core/globals/json.py | 34 +--- psyneulink/core/scheduling/condition.py | 51 ------ psyneulink/core/scheduling/scheduler.py | 13 -- 11 files changed, 9 insertions(+), 450 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index f92c2576655..2b6cbdd614b 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -518,8 +518,8 @@ CONTEXT, CONTROL_PROJECTION, DEFERRED_INITIALIZATION, EXECUTE_UNTIL_FINISHED, \ FUNCTION, FUNCTION_PARAMS, INIT_FULL_EXECUTE_METHOD, INPUT_PORTS, \ LEARNING, LEARNING_PROJECTION, MATRIX, MAX_EXECUTIONS_BEFORE_FINISHED, \ - MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_METADATA, MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PARAMETER_SOURCE, \ - MODEL_SPEC_ID_PARAMETER_VALUE, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_OUTPUT_PORTS, \ + MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_METADATA, \ + MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_OUTPUT_PORTS, \ MODEL_SPEC_ID_MDF_VARIABLE, \ MODULATORY_SPEC_KEYWORDS, NAME, OUTPUT_PORTS, OWNER, PARAMS, PREFS_ARG, \ RESET_STATEFUL_FUNCTION_WHEN, VALUE, VARIABLE @@ -3649,170 +3649,6 @@ def _all_dependent_parameters(obj, filter_name, filter_regex, visited): return _all_dependent_parameters(self, filter_name, filter_regex, set()) - @property - 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.ports.parameterport import ParameterPortError - from psyneulink.core.components.functions.nonstateful.transferfunctions import LinearMatrix - - def parse_parameter_value(value): - if isinstance(value, (list, tuple)): - new_item = [] - for item in value: - new_item.append(parse_parameter_value(item)) - try: - value = type(value)(new_item) - except TypeError: - value = type(value)(*new_item) - elif isinstance(value, dict): - value = { - parse_parameter_value(k): parse_parameter_value(v) - for k, v in value.items() - } - elif isinstance(value, Composition): - value = value.name - elif isinstance(value, Port): - if isinstance(value, OutputPort): - state_port_name = MODEL_SPEC_ID_OUTPUT_PORTS - else: - state_port_name = MODEL_SPEC_ID_INPUT_PORTS - - # assume we will use the identifier on reconstitution - value = '{0}.{1}.{2}'.format( - value.owner.name, - state_port_name, - value.name - ) - elif isinstance(value, Component): - # could potentially create duplicates when it should - # create a reference to an already existent Component like - # with Compositions, but in a vacuum the full specification - # is necessary. - # in fact this would happen unless the parser specifically - # handles it like ours does - value = value._dict_summary - elif isinstance(value, types.FunctionType): - try: - if value is getattr(eval(value.__module__.replace('numpy', 'np')), value.__qualname__): - return f'{value.__module__}.{value.__qualname__}' - except (AttributeError, NameError, TypeError): - pass - value = base64.encodebytes(dill.dumps(value)).decode('utf-8') - - return value - - # attributes (and their values) included in top-level dict - basic_attributes = ['name'] - - # attributes that aren't Parameters but are psyneulink-specific - # and are stored in the PNL parameters section - implicit_parameter_attributes = ['node_ordering', 'required_node_roles', 'excluded_node_roles'] - - parameters_dict = {} - pnl_specific_parameters = {} - deferred_init_values = {} - - if self.initialization_status is ContextFlags.DEFERRED_INIT: - deferred_init_values = copy.copy(self._init_args) - try: - deferred_init_values.update(deferred_init_values['params']) - except (KeyError, TypeError): - pass - - # .parameters still refers to class parameters during deferred init - assert self.parameters._owner is not self - - for p in self.parameters: - if ( - p.name not in self._model_spec_parameter_blacklist - and not isinstance(p, ParameterAlias) - ): - if self.initialization_status is ContextFlags.DEFERRED_INIT: - try: - val = deferred_init_values[p.name] - except KeyError: - # class default - val = p.default_value - else: - # special handling because LinearMatrix default values - # can be PNL-specific keywords. In future, generalize - # this workaround - if ( - isinstance(self, LinearMatrix) - and p.name == 'matrix' - ): - val = self.parameters.matrix.values[None] - elif p.spec is not None: - val = p.spec - else: - val = p.default_value - - val = parse_parameter_value(val) - - try: - matching_parameter_port = self.owner.parameter_ports[p.name] - - if matching_parameter_port.source._owner._owner is self: - val = { - MODEL_SPEC_ID_PARAMETER_SOURCE: '{0}.{1}.{2}'.format( - self.owner.name, - MODEL_SPEC_ID_INPUT_PORTS, - p.name - ), - MODEL_SPEC_ID_PARAMETER_VALUE: val, - MODEL_SPEC_ID_TYPE: type(val) - } - # ContentAddressableList uses TypeError when key not found - except (AttributeError, TypeError, ParameterPortError): - pass - - # split parameters designated as PsyNeuLink-specific and - # parameters that are universal - if p.pnl_internal: - pnl_specific_parameters[p.name] = val - else: - parameters_dict[p.name] = val - - for attr in implicit_parameter_attributes: - try: - pnl_specific_parameters[attr] = getattr(self, attr) - except AttributeError: - pass - - if len(pnl_specific_parameters) > 0: - parameters_dict[MODEL_SPEC_ID_PSYNEULINK] = pnl_specific_parameters - - function_dict = {} - try: - if isinstance(self.function, Component): - function_dict['functions'] = { - self.function.name: self.function._dict_summary - } - except AttributeError: - pass - - type_dict = {} - - if self._model_spec_class_name_is_generic: - type_dict[MODEL_SPEC_ID_GENERIC] = self.__class__.__name__ - else: - if self._model_spec_generic_type_name is not NotImplemented: - type_dict[MODEL_SPEC_ID_GENERIC] = self._model_spec_generic_type_name - else: - type_dict[MODEL_SPEC_ID_GENERIC] = None - - type_dict[MODEL_SPEC_ID_PSYNEULINK] = self.__class__.__name__ - - return { - **{attr: getattr(self, attr) for attr in basic_attributes}, - **{self._model_spec_id_parameters: {k: v for k, v in parameters_dict.items() if 'previous' not in k}}, - **{self._model_spec_id_stateful_parameters: {k: v for k, v in parameters_dict.items() if 'previous' in k}}, - **function_dict, - **{MODEL_SPEC_ID_TYPE: type_dict} - } - def _get_mdf_parameters(self): import modeci_mdf.mdf as mdf diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index ed07a0c8fa1..070cfb30508 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -157,7 +157,7 @@ ARGUMENT_THERAPY_FUNCTION, AUTO_ASSIGN_MATRIX, EXAMPLE_FUNCTION_TYPE, FULL_CONNECTIVITY_MATRIX, FUNCTION_COMPONENT_CATEGORY, FUNCTION_OUTPUT_TYPE, FUNCTION_OUTPUT_TYPE_CONVERSION, HOLLOW_MATRIX, IDENTITY_MATRIX, INVERSE_HOLLOW_MATRIX, NAME, PREFERENCE_SET_NAME, RANDOM_CONNECTIVITY_MATRIX, VALUE, VARIABLE, - MODEL_SPEC_ID_TYPE, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_GENERIC, MODEL_SPEC_ID_METADATA, MODEL_SPEC_ID_MDF_VARIABLE + MODEL_SPEC_ID_METADATA, MODEL_SPEC_ID_MDF_VARIABLE ) from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.basepreferenceset import REPORT_OUTPUT_PREF, is_pref_set @@ -816,20 +816,6 @@ def _model_spec_parameter_blacklist(self): 'multiplicative_param', 'additive_param', }) - @property - def _dict_summary(self): - summary = super()._dict_summary - - try: - type_str = summary[MODEL_SPEC_ID_TYPE][MODEL_SPEC_ID_PSYNEULINK] - except KeyError: - type_str = summary[MODEL_SPEC_ID_TYPE][MODEL_SPEC_ID_GENERIC] - - return { - **summary, - 'function': type_str.lower() - } - def _get_mdf_noise_function(self): import modeci_mdf.mdf as mdf diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index 8afdea8c069..35098ad6924 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -17,7 +17,7 @@ from psyneulink.core.components.functions.function import FunctionError, Function_Base from psyneulink.core.globals.keywords import \ CONTEXT, CUSTOM_FUNCTION, OWNER, PARAMS, \ - SELF, USER_DEFINED_FUNCTION, USER_DEFINED_FUNCTION_TYPE, MODEL_SPEC_ID_PSYNEULINK + SELF, USER_DEFINED_FUNCTION, USER_DEFINED_FUNCTION_TYPE from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences import is_pref_set from psyneulink.core.globals.utilities import _is_module_class, iscompatible @@ -689,35 +689,6 @@ def _gen_llvm_function_body(self, ctx, builder, params, state, builder.position_at_start(post_block) return builder - @property - def _dict_summary(self): - import math - - summary = super()._dict_summary - ext_function_str = None - - try: - import modeci_mdf.functions.standard - # remove import/module errors when modeci_mdf is a package - except (ImportError, ModuleNotFoundError): - pass - else: - if self.custom_function in [ - func_dict['function'] - for name, func_dict - in modeci_mdf.functions.standard.mdf_functions.items() - ]: - ext_function_str = self.custom_function.__name__ - - if _is_module_class(self.custom_function, math): - ext_function_str = f'{self.custom_function.__module__}.{self.custom_function.__name__}' - - if ext_function_str is not None: - summary[self._model_spec_id_parameters][MODEL_SPEC_ID_PSYNEULINK]['custom_function'] = ext_function_str - summary['function'] = ext_function_str - - return summary - def as_mdf_model(self): import math import modeci_mdf.functions.standard diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index aab9893204c..dbe87072fdc 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1104,7 +1104,7 @@ ADDITIVE_PARAM, EXECUTION_PHASE, EXPONENT, FUNCTION_PARAMS, \ INITIALIZING, INIT_EXECUTE_METHOD_ONLY, INIT_FUNCTION_METHOD_ONLY, INPUT, \ INPUT_LABELS_DICT, INPUT_PORT, INPUT_PORT_PARAMS, INPUT_PORTS, MECHANISM, MECHANISM_VALUE, \ - MECHANISM_COMPONENT_CATEGORY, MODEL_SPEC_ID_INPUT_PORTS, MODEL_SPEC_ID_OUTPUT_PORTS, \ + MECHANISM_COMPONENT_CATEGORY, \ MULTIPLICATIVE_PARAM, EXECUTION_COUNT, \ NAME, OUTPUT, OUTPUT_LABELS_DICT, OUTPUT_PORT, OUTPUT_PORT_PARAMS, OUTPUT_PORTS, OWNER_EXECUTION_COUNT, OWNER_VALUE, \ PARAMETER_PORT, PARAMETER_PORT_PARAMS, PARAMETER_PORTS, PROJECTIONS, REFERENCE_VALUE, RESULT, \ @@ -4097,29 +4097,6 @@ def _dependent_components(self): self.parameter_ports, )) - @property - def _dict_summary(self): - inputs_dict = { - MODEL_SPEC_ID_INPUT_PORTS: [ - s._dict_summary for s in self.input_ports - ] - } - inputs_dict[MODEL_SPEC_ID_INPUT_PORTS].extend( - {s.name: s._dict_summary for s in self.parameter_ports} - ) - - outputs_dict = { - MODEL_SPEC_ID_OUTPUT_PORTS: [ - s._dict_summary for s in self.output_ports - ] - } - - return { - **super()._dict_summary, - **inputs_dict, - **outputs_dict - } - def as_mdf_model(self): import modeci_mdf.mdf as mdf diff --git a/psyneulink/core/components/ports/outputport.py b/psyneulink/core/components/ports/outputport.py index 3f8db2d41ad..0094a92e571 100644 --- a/psyneulink/core/components/ports/outputport.py +++ b/psyneulink/core/components/ports/outputport.py @@ -1292,16 +1292,6 @@ def get_label(self, context=None): label_dictionary = {} return self._get_value_label(label_dictionary, self.owner.output_ports, context=context) - @property - def _dict_summary(self): - return { - **super()._dict_summary, - **{ - 'shape': str(self.defaults.value.shape), - 'dtype': str(self.defaults.value.dtype) - } - } - def as_mdf_model(self): import modeci_mdf.mdf as mdf diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index 3f0370c4662..ed2667b6bba 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -2444,17 +2444,6 @@ def _dependent_components(self): )) - @property - def _dict_summary(self): - return { - **super()._dict_summary, - **{ - 'shape': str(self.defaults.variable.shape), - 'dtype': str(self.defaults.variable.dtype) - } - } - - def _instantiate_port_list(owner, port_list, # list of Port specs, (port_spec, params) tuples, or None port_types, # PortType subclass diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 82218f6edfa..cc1a1f81a53 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -1041,43 +1041,6 @@ def _model_spec_parameter_blacklist(self): {'variable'} ) - @property - def _dict_summary(self): - from psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism import CompositionInterfaceMechanism - - # these may occur during deferred init - if not isinstance(self.sender, type): - sender_name = self.sender.name - if isinstance(self.sender.owner, CompositionInterfaceMechanism): - sender_mech = self.sender.owner.composition.name - else: - sender_mech = self.sender.owner.name - else: - sender_name = None - sender_mech = None - - if not isinstance(self.receiver, type): - receiver_name = self.receiver.name - if isinstance(self.receiver.owner, CompositionInterfaceMechanism): - receiver_mech = self.receiver.owner.composition.name - else: - receiver_mech = self.receiver.owner.name - else: - receiver_name = None - receiver_mech = None - - socket_dict = { - MODEL_SPEC_ID_SENDER_PORT: sender_name, - MODEL_SPEC_ID_RECEIVER_PORT: receiver_name, - MODEL_SPEC_ID_SENDER_MECH: sender_mech, - MODEL_SPEC_ID_RECEIVER_MECH: receiver_mech - } - - return { - **super()._dict_summary, - **socket_dict - } - def as_mdf_model(self, simple_edge_format=True): import modeci_mdf.mdf as mdf diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index e306798694b..16a80866908 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -2753,8 +2753,7 @@ def input_function(env, result): DICT, FEEDBACK, FULL, FUNCTION, HARD_CLAMP, IDENTITY_MATRIX, INPUT, INPUT_PORTS, INPUTS, INPUT_CIM_NAME, \ LEARNED_PROJECTIONS, LEARNING_FUNCTION, LEARNING_MECHANISM, LEARNING_MECHANISMS, LEARNING_PATHWAY, \ MATRIX, MATRIX_KEYWORD_VALUES, MAYBE, \ - MODEL_SPEC_ID_COMPOSITION, MODEL_SPEC_ID_NODES, MODEL_SPEC_ID_PROJECTIONS, MODEL_SPEC_ID_PSYNEULINK, MODEL_SPEC_ID_METADATA, \ - MODEL_SPEC_ID_RECEIVER_MECH, MODEL_SPEC_ID_SENDER_MECH, \ + MODEL_SPEC_ID_METADATA, \ MONITOR, MONITOR_FOR_CONTROL, NAME, NESTED, NO_CLAMP, NODE, OBJECTIVE_MECHANISM, ONLINE, OUTCOME, \ OUTPUT, OUTPUT_CIM_NAME, OUTPUT_MECHANISM, OUTPUT_PORTS, OWNER_VALUE, \ PARAMETER, PARAMETER_CIM_NAME, PORT, \ @@ -11417,66 +11416,6 @@ def enable_logging(self): if param.loggable and param.log_condition is LogCondition.OFF: param.log_condition = LogCondition.EXECUTION - @property - def _dict_summary(self): - super_summary = super()._dict_summary - - nodes_dict = {MODEL_SPEC_ID_PSYNEULINK: {}} - projections_dict = {MODEL_SPEC_ID_PSYNEULINK: {}} - - additional_projections = [] - additional_nodes = ( - [self.controller] - if self.controller is not None - else [] - ) - - for n in list(self.nodes) + additional_nodes: - if not isinstance(n, CompositionInterfaceMechanism): - nodes_dict[n.name] = n._dict_summary - - # consider making this more general in the future - try: - additional_projections.extend(n.control_projections) - except AttributeError: - pass - - for p in list(self.projections) + additional_projections: - p_summary = p._dict_summary - # filter projections to/from CIMs of this composition - # and projections to things outside this composition - if ( - ( - p_summary[MODEL_SPEC_ID_SENDER_MECH] != self.name - and p_summary[MODEL_SPEC_ID_RECEIVER_MECH] != self.name - ) - and ( - p_summary[MODEL_SPEC_ID_SENDER_MECH] in nodes_dict - or p_summary[MODEL_SPEC_ID_RECEIVER_MECH] in nodes_dict - ) - ): - projections_dict[p.name] = p_summary - - if len(nodes_dict[MODEL_SPEC_ID_PSYNEULINK]) == 0: - del nodes_dict[MODEL_SPEC_ID_PSYNEULINK] - - if len(projections_dict[MODEL_SPEC_ID_PSYNEULINK]) == 0: - del projections_dict[MODEL_SPEC_ID_PSYNEULINK] - - return { - MODEL_SPEC_ID_COMPOSITION: { - self.name: { - **super_summary, - **self.scheduler._dict_summary, - **{ - MODEL_SPEC_ID_NODES: nodes_dict, - MODEL_SPEC_ID_PROJECTIONS: projections_dict, - 'controller': self.controller, - } - } - } - } - # endregion LLVM def as_mdf_model(self, simple_edge_format=True): diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 37abdcfb028..3d2aad69280 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -181,7 +181,6 @@ """ -import abc import ast import base64 import binascii @@ -222,16 +221,13 @@ class PNLJSONError(Exception): class JSONDumpable: - @property - @abc.abstractmethod - def _dict_summary(self): - pass - @property def json_summary(self): - return _dump_pnl_json_from_dict(self._dict_summary) + return self.as_mdf_model().to_json() +# leaving this due to instructions in test_documentation_models +# (useful for exporting Composition results to JSON) class PNLJSONEncoder(json.JSONEncoder): """ A `JSONEncoder @@ -280,16 +276,6 @@ def default(self, o): return str(o) -def _dump_pnl_json_from_dict(dict_summary): - return json.dumps( - dict_summary, - sort_keys=True, - indent=4, - separators=(',', ': '), - cls=PNLJSONEncoder - ) - - def _get_variable_parameter_name(obj): try: if obj.parameters.variable.mdf_name is not None: @@ -1620,20 +1606,6 @@ def generate_json(*compositions, simple_edge_format=True): model_name = "_".join([c.name for c in compositions]) - merged_graphs_dict_summary = {} - for c in compositions: - if not isinstance(c, Composition): - raise PNLJSONError( - f'Item in compositions arg of {__name__}() is not a Composition: {c}.' - ) - - try: - merged_graphs_dict_summary[MODEL_SPEC_ID_COMPOSITION].update( - c._dict_summary[MODEL_SPEC_ID_COMPOSITION] - ) - except KeyError: - merged_graphs_dict_summary.update(c._dict_summary) - model = mdf.Model( id=model_name, format=f'ModECI MDF v{modeci_mdf.__version__}', diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index f7c23e08676..082288dc41e 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -74,57 +74,6 @@ def is_satisfied(self, *args, context=None, execution_id=None, **kwargs): **kwargs ) - @property - def _dict_summary(self): - from psyneulink.core.components.component import Component - - if type(self) in {graph_scheduler.Condition, Condition}: - try: - func_val = inspect.getsource(self.func) - except OSError: - func_val = dill.dumps(self.func) - else: - func_val = None - - extra_args = { - 'function': func_val, - **self.kwargs, - } - - sig = inspect.signature(self.__init__) - - for name, param in sig.parameters.items(): - if param.kind is inspect.Parameter.VAR_POSITIONAL: - args_list = [] - for a in self.args: - if isinstance(a, Component): - a = a.name - elif isinstance(a, graph_scheduler.Condition): - a = a._dict_summary - args_list.append(a) - extra_args[name] = args_list - - for i, (name, param) in enumerate(filter( - lambda item: item[1].kind is inspect.Parameter.POSITIONAL_OR_KEYWORD and item[0] not in self.kwargs, - sig.parameters.items() - )): - try: - extra_args[name] = self.args[i] - except IndexError: - # was specified with keyword not as positional arg - extra_args[name] = param.default - - for name in extra_args: - if isinstance(extra_args[name], Component): - extra_args[name] = extra_args[name].name - elif isinstance(extra_args[name], graph_scheduler.Condition): - extra_args[name] = extra_args[name]._dict_summary - - return { - MODEL_SPEC_ID_TYPE: self.__class__.__name__, - 'args': extra_args - } - def as_mdf_model(self): import modeci_mdf.mdf as mdf from psyneulink.core.components.component import Component diff --git a/psyneulink/core/scheduling/scheduler.py b/psyneulink/core/scheduling/scheduler.py index 65780f4b6f3..ce265986788 100644 --- a/psyneulink/core/scheduling/scheduler.py +++ b/psyneulink/core/scheduling/scheduler.py @@ -96,19 +96,6 @@ def run( skip_environment_state_update_time_increment=skip_trial_time_increment, ) - @property - def _dict_summary(self): - return { - 'conditions': { - 'termination': { - str.lower(k.name): v._dict_summary for k, v in self.termination_conds.items() - }, - 'node_specific': { - n.name: self.conditions[n]._dict_summary for n in self.nodes if n in self.conditions - } - } - } - def as_mdf_model(self): import modeci_mdf.mdf as mdf From 44d875eb6b0b6c4a0760f1ad1a0d09485eb67b8a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 25 Feb 2022 23:47:52 -0500 Subject: [PATCH 244/285] JSON: add generate_script_from_mdf function --- psyneulink/core/globals/json.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 3d2aad69280..fbce7dad615 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -211,7 +211,7 @@ __all__ = [ 'PNLJSONError', 'JSONDumpable', 'PNLJSONEncoder', - 'generate_json', 'generate_script_from_json', + 'generate_json', 'generate_script_from_json', 'generate_script_from_mdf', 'write_json_file' ] @@ -1582,6 +1582,29 @@ def get_declared_identifiers(graphs_dict): return model_output +def generate_script_from_mdf(model_input, outfile=None): + """ + Generate a Python script from MDF model **model_input** + + .. warning:: + Use of `generate_script_from_mdf` to generate a Python script from a model without taking proper precautions + can introduce a security risk to the system on which the Python interpreter is running. This is because it + calls exec, which has the potential to execute non-PsyNeuLink-related code embedded in the file. Therefore, + `generate_script_from_mdf` should be used to read only model of known and secure origin. + + Arguments + --------- + + model_input : modeci_mdf.Model + + Returns + ------- + + Text of Python script : str + """ + return generate_script_from_json(model_input.to_json(), outfile) + + def generate_json(*compositions, simple_edge_format=True): """ Generate the `general JSON format ` From ff96f910a881bb9c9f4832dc162cee14ed147ecb Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 4 Mar 2022 22:27:19 -0500 Subject: [PATCH 245/285] JSON: support Threshold in MDF --- psyneulink/core/globals/json.py | 7 +++++++ psyneulink/core/scheduling/condition.py | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index fbce7dad615..a93160525ab 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -918,6 +918,13 @@ def _parse_condition_arg_value(value): else: return _generate_condition_string(value, component_identifiers) + # handle value/outputport fix for threshold + try: + if re.match(r'\w+_OutputPort_0', value): + return '"value"' + except TypeError: + pass + return str(_parse_parameter_value(value, component_identifiers)) def _parse_graph_scheduler_type(typ): diff --git a/psyneulink/core/scheduling/condition.py b/psyneulink/core/scheduling/condition.py index 082288dc41e..aba519892b7 100644 --- a/psyneulink/core/scheduling/condition.py +++ b/psyneulink/core/scheduling/condition.py @@ -288,3 +288,11 @@ def func(threshold, comparator, indices, atol, rtol, execution_id): atol=atol, rtol=rtol, ) + + def as_mdf_model(self): + m = super().as_mdf_model() + + if self.parameter == 'value': + m.args['parameter'] = f'{self.dependency.name}_OutputPort_0' + + return m From 9137bd3551a7df07f663d00c5aa48fa1e53b9b8c Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 25 Feb 2022 23:48:02 -0500 Subject: [PATCH 246/285] JSON: rebrand documentation to MDF --- docs/source/Core.rst | 2 +- docs/source/{json.rst => mdf.rst} | 4 +- psyneulink/core/globals/json.py | 139 +++--------------------------- 3 files changed, 16 insertions(+), 129 deletions(-) rename docs/source/{json.rst => mdf.rst} (91%) diff --git a/docs/source/Core.rst b/docs/source/Core.rst index e66bd4c40ee..292689884fd 100644 --- a/docs/source/Core.rst +++ b/docs/source/Core.rst @@ -93,4 +93,4 @@ Core - `Compilation` - `Report` - `Log` - - `json` + - `mdf` diff --git a/docs/source/json.rst b/docs/source/mdf.rst similarity index 91% rename from docs/source/json.rst rename to docs/source/mdf.rst index cd95ba8bf26..5f1fd9cad69 100644 --- a/docs/source/json.rst +++ b/docs/source/mdf.rst @@ -1,5 +1,5 @@ -JSON -==== +MDF +=== .. automodule:: psyneulink.core.globals.json :members: diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index a93160525ab..5b934b3e1e4 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -16,26 +16,29 @@ The developers of PsyNeuLink are collaborating with the scientific community, as part of the `OpenNeuro effort `_, to create a standard, JSON-based format for the description and exchange of computational models of brain and psychological function across different simulation environments. As part of this effort, -PsyNeuLink includes the ability to export models into, and import valid Python scripts that express a PsyNeuLink -model from this JSON format. +PsyNeuLink supports the `ModECI Model Description Format `_ (MDF) by +including the ability to produce an MDF-compatible model from a PsyNeuLink model and to construct valid Python +scripts that express a PsyNeuLink model from an MDF model. -Any PsyNeuLink `Composition` or `Component` can be exported to the JSON format using its `json_summary` method, that -uses its `_dict_summary `. This generates a string that, passed into the +Any PsyNeuLink `Composition` or `Component` can be exported to MDF format using its `as_mdf_model` method or +to JSON format using its `json_summary` method. `json_summary` generates a string that, passed into the `generate_script_from_json` function, produces a valid Python script replicating the original PsyNeuLink model. `write_json_file` can be used to write the json_summary for one or more Compositions into a specified file (though see `note `). `generate_script_from_json` can accept either the string returned by `generate_script_from_json` or the name of a file containing one. Calling ``exec(generate_script_from_json())`` will load into the current namespace all of the PsyNeuLink objects specified in the ``input``; and `get_compositions` can be used to retrieve a list of all of the Compositions -in that namespace, including any generated by execution of `generate_script_from_json`. +in that namespace, including any generated by execution of `generate_script_from_json`. `generate_script_from_mdf` +may similarly be used to create a PsyNeuLink Python script from a ModECI MDF Model object, such as that created +by `as_mdf_model `. .. _JSON_Security_Warning: .. warning:: - Use of `generate_script_from_json` to generate a Python script from a file without taking proper precautions can + Use of `generate_script_from_json` or `generate_script_from_mdf` to generate a Python script from a file without taking proper precautions can introduce a security risk to the system on which the Python interpreter is running. This is because it calls exec, which has the potential to execute non-PsyNeuLink-related code embedded in the file. Therefore, - `generate_script_from_json` should be used to read only files of known and secure origin. + `generate_script_from_json` or `generate_script_from_mdf` should be used to read only files of known and secure origin. .. _JSON_Examples: @@ -54,129 +57,13 @@ .. _JSON_Model_Specification: -JSON Model Specification +JSON/MDF Model Specification ------------------------ .. note:: - The JSON format is in early development, and is subject to change. + The format is in development, and is subject to change. - -The outermost level of a JSON model is a dictionary with entry ``graphs``, a list of Composition objects. - -Each Component's JSON object contains multiple entries. Those that are common to all are: - -* ``name`` : a label for the Component - -* ``parameters`` (non-`Function`\\ s) / ``args`` (`Function`\\ s) : a dictionary where each entry is either a - `Parameter` name and value, or a subdictionary of modeling-environment specific parameters. For PsyNeuLink, - this is indicated by `PNL`: - - -.. code-block:: javascript - - "args": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "variable": [ - 0.01 - ] - }, - "bounds": null, - "intercept": 0.0, - "slope": 1.0 - } - -Note that the value of a parameter may be a long-form dictionary when it corresponds to a `ParameterPort`. -In this case, it will indicate the ParameterPort in a `source<>` field: - -.. code-block:: javascript - - "intercept": { - "source": "A.input_ports.intercept", - "type": "float", - "value": 2.0 - } - -* ``type`` : a dictionary with entries based on modeling environment to describe the type of the object. - The `generic` entry is populated if the object has a universal name (such as a linear function). - Modeling-environment-specific entries are populated when relevant. - -.. code-block:: javascript - - "type": { - "PNL": "Composition", - "generic": "graph" - } - - -**Mechanisms**, **Projections**, and **Ports** each have: - -* ``functions`` : a list of primary `Function` JSON objects. In \ -PsyNeuLink, only one primary function is allowed. - -.. code-block:: javascript - - "functions": [ - { - "args": { - "intercept": { - "source": "A.input_ports.intercept", - "type": "float", - "value": 2.0 - }, - "slope": { - "source": "A.input_ports.slope", - "type": "float", - "value": 5.0 - } - }, - "name": "Linear Function-1", - "type": { - "generic": "Linear" - } - } - ] - -**Mechanisms** have: - -* ``input_ports`` : a list of InputPort and ParameterPort JSON objects - -* ``output_ports`` : a list of OutputPort JSON objects - -**Projections** have: - -* ``sender`` : the name of the Component it projects from - -* ``sender_port`` : the name of the port on the ``sender`` to which it \ -connects - -* ``receiver`` : the name of the Component it projects to - -* ``receiver_port`` : the name of the port on the ``receiver`` to \ -which it connects - -**Ports** have: - -* ``dtype`` : the type of accepted input/output for the Port. This \ -corresponds to `numpy.dtype `_ - -* ``shape`` : the shape of the accepted input/output. This corresponds \ -to numpy ndarray shapes. (`numpy.zeros()` would produce an \ -array with the correct shape) - -**Compositions** have: - -* ``nodes`` : a dictionary of Mechanisms or Compositions keyed on \ -their names that are part of the Composition - -* ``edges`` : a dictionary of Projections keyed on their names that \ -connect nodes of the Composition - -* ``controller`` : the name of the Mechanism in the Composition's \ -nodes that serves as the Composition's \ -`controller `, if it exists +See https://github.com/ModECI/MDF/blob/main/docs/README.md#model """ From b839f462a0a0e805261a037317714aa0e300e01e Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 9 Mar 2022 22:48:12 -0500 Subject: [PATCH 247/285] docs: JSON: update stroop mdf example --- .../_static/stroop_conflict_monitoring.json | 9365 +++++------------ 1 file changed, 2669 insertions(+), 6696 deletions(-) diff --git a/docs/source/_static/stroop_conflict_monitoring.json b/docs/source/_static/stroop_conflict_monitoring.json index aa3962a1454..bc5ced098b6 100644 --- a/docs/source/_static/stroop_conflict_monitoring.json +++ b/docs/source/_static/stroop_conflict_monitoring.json @@ -1,7040 +1,3013 @@ { - "graphs": [ - { - "controller": "CONTROL", - "edges": { - "ControlProjection for TASK[gain]": { - "functions": [ - { + "Stroop_model": { + "format": "ModECI MDF v0.3.1", + "generating_application": "PsyNeuLink v0.10.0.0+488.g0294251622", + "graphs": { + "Stroop_model": { + "parameters": {}, + "conditions": { + "node_specific": { + "color_hidden": { + "type": "EveryNCalls", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": { - "source": "ControlProjection for TASK[gain].input_ports.intercept", - "type": "float", - "value": 0.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": { - "source": "ControlProjection for TASK[gain].input_ports.slope", - "type": "float", - "value": 1.0 - } - }, - "name": "Linear Function-81", - "type": { - "generic": "Linear" + "dependency": "TASK", + "n": 10 } - } - ], - "name": "ControlProjection for TASK[gain]", - "parameters": { - "PNL": { - "control_signal": null, - "control_signal_params": null, - "execution_count": 0, - "has_initializers": false }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "TASK", - "receiver_port": "gain", - "sender": "CONTROL", - "sender_port": "TASK[gain] ControlSignal", - "type": { - "PNL": "ControlProjection", - "generic": null - } - }, - "MappingProjection from Conflict Monitor[OUTCOME] to CONTROL[OUTCOME]": { - "functions": [ - { + "word_hidden": { + "type": "EveryNCalls", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from Conflict Monitor[OUTCOME] to CONTROL[OUTCOME].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 1.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-2", - "type": { - "PNL": "LinearMatrix", - "generic": null + "dependency": "TASK", + "n": 10 } - } - ], - "name": "MappingProjection from Conflict Monitor[OUTCOME] to CONTROL[OUTCOME]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 1.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "CONTROL", - "receiver_port": "OUTCOME", - "sender": "Conflict Monitor", - "sender_port": "OUTCOME", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "MappingProjection from OUTPUT[OutputPort-0] to Conflict Monitor[InputPort-0]": { - "functions": [ - { + "OUTPUT": { + "type": "All", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5, - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from OUTPUT[OutputPort-0] to Conflict Monitor[InputPort-0].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 1.0, - 0.0 - ], - [ - 0.0, - 1.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-1", - "type": { - "PNL": "LinearMatrix", - "generic": null + "args": [ + { + "type": "EveryNCalls", + "args": { + "dependency": "color_hidden", + "n": 1 + } + }, + { + "type": "EveryNCalls", + "args": { + "dependency": "word_hidden", + "n": 1 + } + } + ] } - } - ], - "name": "MappingProjection from OUTPUT[OutputPort-0] to Conflict Monitor[InputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 1.0, - 0.0 - ], - [ - 0.0, - 1.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "Conflict Monitor", - "receiver_port": "Value of OUTPUT [OutputPort-0]", - "sender": "OUTPUT", - "sender_port": "OutputPort-0", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "MappingProjection from OUTPUT[OutputPort-0] to DECISION[ARRAY]": { - "functions": [ - { + "DECISION": { + "type": "EveryNCalls", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5, - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from OUTPUT[OutputPort-0] to DECISION[ARRAY].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 1.0, - 0.0 - ], - [ - 0.0, - 1.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-10", - "type": { - "PNL": "LinearMatrix", - "generic": null + "dependency": "OUTPUT", + "n": 1 } } - ], - "name": "MappingProjection from OUTPUT[OutputPort-0] to DECISION[ARRAY]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false - }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 1.0, - 0.0 - ], - [ - 0.0, - 1.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null }, - "receiver": "DECISION", - "receiver_port": "ARRAY", - "sender": "OUTPUT", - "sender_port": "OutputPort-0", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "MappingProjection from TASK[RESULT] to color_hidden[InputPort-0]": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.6283161882953663, - 0.6283161882953663 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from TASK[RESULT] to color_hidden[InputPort-0].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 4.0, - 4.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-8", - "type": { - "PNL": "LinearMatrix", - "generic": null - } - } - ], - "name": "MappingProjection from TASK[RESULT] to color_hidden[InputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false + "termination": { + "environment_sequence": { + "type": "Never", + "args": {} }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 4.0, - 4.0 - ], - [ - 0.0, - 0.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "color_hidden", - "receiver_port": "InputPort-0", - "sender": "TASK", - "sender_port": "RESULT", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "MappingProjection from TASK[RESULT] to word_hidden[InputPort-0]": { - "functions": [ - { + "environment_state_update": { + "type": "AllHaveRun", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.6283161882953663, - 0.6283161882953663 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from TASK[RESULT] to word_hidden[InputPort-0].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 0.0, - 0.0 - ], - [ - 4.0, - 4.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-9", - "type": { - "PNL": "LinearMatrix", - "generic": null + "dependencies": [] } } - ], - "name": "MappingProjection from TASK[RESULT] to word_hidden[InputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false - }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 0.0, - 0.0 - ], - [ - 4.0, - 4.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "word_hidden", - "receiver_port": "InputPort-0", - "sender": "TASK", - "sender_port": "RESULT", - "type": { - "PNL": "MappingProjection", - "generic": null } }, - "MappingProjection from color_hidden[OutputPort-0] to OUTPUT[InputPort-0]": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.017986209962091562, - 0.017986209962091562 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from color_hidden[OutputPort-0] to OUTPUT[InputPort-0].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 2.0, - -2.0 - ], - [ - -2.0, - 2.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-4", - "type": { - "PNL": "LinearMatrix", - "generic": null - } - } + "metadata": { + "type": "Composition", + "input_specification": null, + "has_initializers": false, + "variable": [ + 0 ], - "name": "MappingProjection from color_hidden[OutputPort-0] to OUTPUT[InputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false - }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 2.0, - -2.0 - ], - [ - -2.0, - 2.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "OUTPUT", - "receiver_port": "InputPort-0", - "sender": "color_hidden", - "sender_port": "OutputPort-0", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "MappingProjection from color_input[OutputPort-0] to color_hidden[InputPort-0]": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0, - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from color_input[OutputPort-0] to color_hidden[InputPort-0].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 2.0, - -2.0 - ], - [ - -2.0, - 2.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-3", - "type": { - "PNL": "LinearMatrix", - "generic": null - } - } + "simulation_results": [], + "execute_until_finished": true, + "retain_old_simulation_data": false, + "max_executions_before_finished": 1000, + "results": [], + "node_ordering": [ + "color_input", + "color_hidden", + "OUTPUT", + "word_input", + "word_hidden", + "task_input", + "TASK", + "DECISION", + "Conflict_Monitor", + "CONTROL" ], - "name": "MappingProjection from color_input[OutputPort-0] to color_hidden[InputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false - }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 2.0, - -2.0 - ], - [ - -2.0, - 2.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "color_hidden", - "receiver_port": "InputPort-0", - "sender": "color_input", - "sender_port": "OutputPort-0", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "MappingProjection from task_input[OutputPort-0] to TASK[InputPort-0]": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0, - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from task_input[OutputPort-0] to TASK[InputPort-0].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 1.0, - 0.0 - ], - [ - 0.0, - 1.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-7", - "type": { - "PNL": "LinearMatrix", - "generic": null - } - } + "required_node_roles": [ + [ + "Conflict_Monitor", + "NodeRole.CONTROLLER_OBJECTIVE" + ] ], - "name": "MappingProjection from task_input[OutputPort-0] to TASK[InputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false - }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 1.0, - 0.0 - ], - [ - 0.0, - 1.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "TASK", - "receiver_port": "InputPort-0", - "sender": "task_input", - "sender_port": "OutputPort-0", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "MappingProjection from word_hidden[OutputPort-0] to OUTPUT[InputPort-0]": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.017986209962091562, - 0.017986209962091562 + "controller": { + "CONTROL": { + "metadata": { + "type": "ControlMechanism", + "output_labels_dict": {}, + "modulation": "multiplicative_param", + "input_port_variables": null, + "has_initializers": false, + "outcome": null, + "variable": [ + [ + 1.0 ] - }, - "bounds": null, + ], "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from word_hidden[OutputPort-0] to OUTPUT[InputPort-0].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 3.0, - -3.0 - ], - [ - -3.0, - 3.0 - ] - ] - }, + "input_labels_dict": {}, + "simulation_ids": [], "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-6", - "type": { - "PNL": "LinearMatrix", - "generic": null - } - } - ], - "name": "MappingProjection from word_hidden[OutputPort-0] to OUTPUT[InputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false - }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 3.0, - -3.0 - ], - [ - -3.0, - 3.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "OUTPUT", - "receiver_port": "InputPort-0", - "sender": "word_hidden", - "sender_port": "OutputPort-0", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "MappingProjection from word_input[OutputPort-0] to word_hidden[InputPort-0]": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0, - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "MappingProjection from word_input[OutputPort-0] to word_hidden[InputPort-0].input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 3.0, - -3.0 - ], - [ - -3.0, - 3.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-5", - "type": { - "PNL": "LinearMatrix", - "generic": null - } - } - ], - "name": "MappingProjection from word_input[OutputPort-0] to word_hidden[InputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false - }, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "matrix": [ - [ - 3.0, - -3.0 - ], - [ - -3.0, - 3.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "word_hidden", - "receiver_port": "InputPort-0", - "sender": "word_input", - "sender_port": "OutputPort-0", - "type": { - "PNL": "MappingProjection", - "generic": null - } - }, - "TASK recurrent projection": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.6283161882953663, - 0.6283161882953663 - ] - }, - "bounds": null, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": { - "source": "TASK recurrent projection.input_ports.matrix", - "type": "numpy.array", - "value": [ - [ - 0.0, - -1.0 - ], - [ - -1.0, - 0.0 - ] - ] - }, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0 - }, - "name": "LinearMatrix Function-0", - "type": { - "PNL": "LinearMatrix", - "generic": null - } - } - ], - "name": "TASK recurrent projection", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false - }, - "auto": 1, - "execute_until_finished": true, - "exponent": null, - "hetero": 0, - "is_finished_flag": true, - "matrix": [ - [ - 0.0, - -1.0 - ], - [ - -1.0, - 0.0 - ] - ], - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "weight": null - }, - "receiver": "TASK", - "receiver_port": "InputPort-0", - "sender": "TASK", - "sender_port": "RESULT", - "type": { - "PNL": "AutoAssociativeProjection", - "generic": null - } - } - }, - "name": "Stroop_model", - "nodes": { - "CONTROL": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 1.0 - ] - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_control_signals": 1, - "num_executions_before_finished": 0 - }, - "name": "Default Control Function-0", - "type": { - "PNL": "DefaultAllocationFunction", - "generic": null - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-85", - "type": { - "PNL": "LinearCombination", - "generic": null - } - } - ], - "name": "OUTCOME", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - 1.0 - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(1,)", - "type": { - "PNL": "InputPort", - "generic": null - } - } - ], - "name": "CONTROL", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "LinearCombination": "LinearCombination", - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3 - }, - "SimpleIntegrator": "SimpleIntegrator", - "adjustment_cost": null, - "adjustment_cost_fct": "Linear", - "adjustment_cost_fct_add_param": 0.0, - "adjustment_cost_fct_mult_param": 1.0, - "bounds": null, - "combine_costs_fct": "Reduce", - "combine_costs_fct_add_param": 0.0, - "combine_costs_fct_mult_param": 1.0, - "combined_costs": null, - "duration_cost": null, - "duration_cost_fct": "SimpleIntegrator", - "duration_cost_fct_add_param": 0.0, - "duration_cost_fct_mult_param": 0.0, - "enabled_cost_functions": 2, - "execute_until_finished": true, - "intensity": [ - 0 - ], - "intensity_cost": [ - 1.6487212707001282 - ], - "intensity_cost_fct": "Exponential", - "intensity_cost_fct_add_param": 0.0, - "intensity_cost_fct_mult_param": 1.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "transfer_fct": "Linear", - "transfer_fct_add_param": null, - "transfer_fct_mult_param": null, - "variable": [ - 0.5 - ] - }, - "name": "TransferWithCosts Function-0", - "type": { - "PNL": "TransferWithCosts", - "generic": null - } - } - ], - "name": "TASK[gain] ControlSignal", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5 - ] - }, - "adjustment_cost": 0, - "adjustment_cost_function": "Linear", - "allocation_samples": null, - "combine_costs_function": "Reduce", - "cost": null, - "cost_options": 2, - "duration_cost": 0, - "duration_cost_function": "SimpleIntegrator", - "execute_until_finished": true, - "intensity_cost": null, - "intensity_cost_function": "Exponential", - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "modulation": "multiplicative_param", - "num_executions_before_finished": 0, - "projections": [ - [ - "TASK.input_ports.gain", - null, - null, - { - "PROJECTION_TYPE": "ControlProjection" - } - ] - ], - "transfer_function": "Linear" - }, - "shape": "(1,)", - "type": { - "PNL": "ControlSignal", - "generic": null - } - } - ], - "parameters": { - "PNL": { - "control_signal_costs": null, - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "modulation": "multiplicative_param", - "net_outcome": null, - "outcome": null, - "output_labels_dict": {}, - "previous_value": null, - "simulation_ids": [], - "variable": [ - [ - 1.0 - ] - ] - }, - "combine_costs": "gANjbnVtcHkKc3VtCnEALg==\n", - "compute_net_outcome": "gANjZGlsbC5fZGlsbApfY3JlYXRlX2Z1bmN0aW9uCnEAKGNkaWxsLl9kaWxsCl9sb2FkX3R5cGUK\ncQFYCAAAAENvZGVUeXBlcQKFcQNScQQoSwJLAEsCSwJLQ0MIfAB8ARgAUwBxBU6FcQYpWAcAAABv\ndXRjb21lcQdYBAAAAGNvc3RxCIZxCVhsAAAAL2hvbWUva2F0aGVyaW5lL2NvZGUvUHN5TmV1TGlu\nay9wc3luZXVsaW5rL2NvcmUvY29tcG9uZW50cy9tZWNoYW5pc21zL21vZHVsYXRvcnkvY29udHJv\nbC9jb250cm9sbWVjaGFuaXNtLnB5cQpYCAAAADxsYW1iZGE+cQtNQQRDAHEMKSl0cQ1ScQ5jcHN5\nbmV1bGluay5jb3JlLmNvbXBvbmVudHMubWVjaGFuaXNtcy5tb2R1bGF0b3J5LmNvbnRyb2wuY29u\ndHJvbG1lY2hhbmlzbQpfX2RpY3RfXwpoC05OfXEPTnRxEFJxES4=\n", - "compute_reconfiguration_cost": null, - "costs": null, - "default_allocation": [ - 0.5 - ], - "execute_until_finished": true, - "input_ports": [ - "OUTCOME" - ], - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "monitor_for_control": [], - "num_executions_before_finished": 0, - "objective_mechanism": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, + "control_signal_costs": null, + "net_outcome": null, + "reconfiguration_cost": null, + "objective_mechanism": { + "Conflict_Monitor": { + "metadata": { + "type": "ObjectiveMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, "has_initializers": false, - "output_type": 3, "variable": [ [ 0.0, 0.0 ] - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": [ - [ - 0, - -2.5 ], - [ - -2.5, - 0 + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": [ + { + "OUTPUT": { + "metadata": { + "type": "ProcessingMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null + }, + "input_ports": { + "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + }, + "OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + } + }, + "functions": { + "OUTPUT_input_combination_function": { + "function": "onnx::ReduceSum", + "args": { + "data": "combination_function_input_data", + "axes": 0 + } + }, + "OUTPUT_input_combination_function_dimreduce": { + "value": "variable0[0][0]", + "args": { + "variable0": "OUTPUT_input_combination_function" + } + }, + "Logistic_Function_1": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": 0.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "OUTPUT_input_combination_function_dimreduce" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, + "has_initializers": false, + "enable_output_type_conversion": true, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} + } + } + }, + "parameters": { + "combination_function_input_data": { + "value": "[OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_, OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_]" + } + }, + "output_ports": { + "OUTPUT_OutputPort_0": { + "value": "Logistic_Function_1", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } + } + } + } + } + ], + "output_ports": [ + "OUTCOME" ] - ], - "max_executions_before_finished": 1000, - "metric": "energy", - "metric_fct": null, - "normalize": false, - "num_executions_before_finished": 0, - "transfer_fct": null - }, - "name": "Stability Function-0", - "type": { - "PNL": "Energy", - "generic": null - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, + }, + "input_ports": { + "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_": { + "shape": "(1, 2)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, "has_initializers": false, - "output_type": 3, + "internal_only": true, "variable": [ [ 0.0, 0.0 ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-77", - "type": { - "PNL": "LinearCombination", - "generic": null + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": [ + [ + "OUTPUT.output_ports.OutputPort-0", + null, + null, + { + "PROJECTION_TYPE": "MappingProjection" + } + ] + ], + "weight": null, + "default_input": null + } } - } - ], - "name": "Value of OUTPUT [OutputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": [ - [ - "OUTPUT.output_ports.OutputPort-0", - null, - null, - { - "PROJECTION_TYPE": "MappingProjection" - } - ] - ], - "weight": null - }, - "shape": "(1, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - } - ], - "name": "Conflict Monitor", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, + "functions": { + "Stability_Function_0": { + "function": "energy", + "args": { + "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" + }, + "metadata": { + "type": "Energy", + "max_executions_before_finished": 1000, + "changes_shape": false, "has_initializers": false, - "output_type": 3, "variable": [ - -0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-71", - "type": { - "generic": "Linear" - } - } - ], - "name": "OUTCOME", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - -0.0 - ] + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "enable_output_type_conversion": true, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "matrix": [ + [ + 0, + -2.5 + ], + [ + -2.5, + 0 + ] + ], + "metric": "energy", + "transfer_fct": null, + "metric_fct": { + "Distance_Function_4": { + "function": "distance", + "args": {}, + "metadata": { + "type": "Distance", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "variable": [ + [ + [ + 0.0, + 0.0 + ] + ], + [ + [ + 0.0, + 0.0 + ] + ] + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "metric": "energy", + "normalize": false, + "function_stateful_params": {} + } + } + }, + "normalize": false, + "function_stateful_params": {} + } + } }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "OutputPort", - "generic": null + "output_ports": { + "Conflict_Monitor_OUTCOME": { + "value": "Stability_Function_0", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + -0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } + } + } } - } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] }, - "execute_until_finished": true, - "input_ports": [ + "outcome_input_ports_option": "separate", + "default_allocation": [ + 0.5 + ], + "output_ports": [ { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] + "name": "gain", + "MECHANISM": { + "TASK": { + "metadata": { + "type": "LCAMechanism", + "output_labels_dict": {}, + "input_port_variables": null, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 ] - }, - "bias": { - "source": "OUTPUT.input_ports.bias", - "type": "float", - "value": 0.0 - }, - "bounds": null, + ], "execute_until_finished": true, - "gain": { - "source": "OUTPUT.input_ports.gain", - "type": "float", - "value": 1.0 - }, - "is_finished_flag": true, + "input_labels_dict": {}, "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": { - "source": "OUTPUT.input_ports.offset", - "type": "float", - "value": 0.0 - }, - "scale": { - "source": "OUTPUT.input_ports.scale", - "type": "float", - "value": 1.0 - }, - "x_0": { - "source": "OUTPUT.input_ports.x_0", - "type": "float", - "value": 0.0 - } - }, - "name": "Logistic Function-3", - "type": { - "generic": "Logistic" - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, + "termination_measure_value": 0.0, + "integrator_function_value": [ + [ + 0 + ] + ], + "output_ports": [ + "RESULTS" + ], + "termination_threshold": null, + "clip": null, + "has_recurrent_input_port": false, + "termination_measure": "max", + "input_ports": null, + "matrix": "InverseHollowMatrix", + "termination_comparison_op": ">=", + "integrator_mode": true, + "combination_function": { + "LinearCombination_Function_23": { + "function": "linearcombination", + "args": { + "offset": 0.0, + "scale": 1.0 + }, + "metadata": { + "type": "LinearCombination", + "changes_shape": false, "has_initializers": false, - "output_type": 3, + "enable_output_type_conversion": false, "variable": [ [ 0.0, 0.0 - ], + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.DEFAULT", + "weights": null, + "operation": "sum", + "exponents": null, + "function_stateful_params": {} + } + } + }, + "on_resume_integrator_mode": "current_value", + "enable_learning": false, + "integrator_function": { + "LeakyCompetingIntegrator_Function_0": { + "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", + "args": { + "rate": 0.5, + "time_step_size": 0.1, + "offset": 0.0, + "noise": 0.0 + }, + "metadata": { + "type": "LeakyCompetingIntegrator", + "initializer": [ + [ + 0.5, + 0.5 + ] + ], + "changes_shape": false, + "has_initializers": true, + "enable_output_type_conversion": false, + "variable": [ [ 0.0, 0.0 ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-33", - "type": { - "PNL": "LinearCombination", - "generic": null + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.DEFAULT", + "function_stateful_params": { + "previous_value": { + "id": "previous_value", + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } + } + } } } - ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] + }, + "input_ports": { + "TASK_input_port_TASK_recurrent_projection": { + "shape": "(2,)", + "type": "float64" }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null + "TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + } }, - "shape": "(2, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { + "functions": { + "TASK_input_combination_function": { + "function": "onnx::ReduceSum", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-15", - "type": { - "generic": "Linear" + "data": "combination_function_input_data", + "axes": 0 } - } - ], - "name": "scale", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { + "TASK_input_combination_function_dimreduce": { + "value": "variable0[0][0]", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ + "variable0": "TASK_input_combination_function" + } + }, + "Logistic_Function_5": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": 0.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "LeakyCompetingIntegrator_Function_0" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, + "has_initializers": false, + "enable_output_type_conversion": true, + "variable": [ + [ + 0.0, 0.0 ] - }, - "bounds": null, + ], "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-16", - "type": { - "generic": "Linear" + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} } - } - ], - "name": "offset", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { + "LeakyCompetingIntegrator_Function_0": { + "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ + "rate": 0.5, + "time_step_size": 0.1, + "offset": 0.0, + "noise": 0.0, + "variable0": "TASK_input_combination_function_dimreduce" + }, + "metadata": { + "type": "LeakyCompetingIntegrator", + "initializer": [ + [ + 0.5, + 0.5 + ] + ], + "changes_shape": false, + "has_initializers": true, + "enable_output_type_conversion": false, + "variable": [ + [ + 0.0, 0.0 ] - }, - "bounds": null, + ], "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-17", - "type": { - "generic": "Linear" + "output_type": "FunctionOutputType.DEFAULT", + "function_stateful_params": { + "previous_value": { + "id": "previous_value", + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } + } } } - ], - "name": "x_0", + }, "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] + "auto": { + "value": 0.0 }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null + "competition": { + "value": 1.0 + }, + "smoothing_factor": { + "value": 0.5 + }, + "hetero": { + "value": -1.0 + }, + "combination_function_input_data": { + "value": "[TASK_input_port_TASK_recurrent_projection, TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_]" + }, + "previous_value": { + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, + "output_ports": { + "TASK_RESULT": { + "value": "Logistic_Function_5", + "metadata": { + "type": "OutputPort", "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-18", - "type": { - "generic": "Linear" + "has_initializers": false, + "variable": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null } } - ], - "name": "gain", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-19", - "type": { - "generic": "Linear" - } - } - ], - "name": "bias", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - } - ], - "name": "OUTPUT", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5, - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-21", - "type": { - "generic": "Linear" - } - } - ], - "name": "OutputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5, - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null } } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "input_ports": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": null - }, - "type": { - "PNL": "ProcessingMechanism", - "generic": null } } ], - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": [ + "costs": null, + "compute_reconfiguration_cost": null, + "combine_costs": "gANjbnVtcHkKc3VtCnEALg==\n", + "input_ports": [ "OUTCOME" + ], + "outcome_input_ports": "[(InputPort OUTCOME)]", + "monitor_for_control": [], + "compute_net_outcome": "gANjZGlsbC5fZGlsbApfY3JlYXRlX2Z1bmN0aW9uCnEAKGNkaWxsLl9kaWxsCl9jcmVhdGVfY29k\nZQpxAShLAksASwJLAktDQwh8AHwBGABTAHECToVxAylYBwAAAG91dGNvbWVxBFgEAAAAY29zdHEF\nhnEGWGwAAAAvaG9tZS9rYXRoZXJpbmUvY29kZS9Qc3lOZXVMaW5rL3BzeW5ldWxpbmsvY29yZS9j\nb21wb25lbnRzL21lY2hhbmlzbXMvbW9kdWxhdG9yeS9jb250cm9sL2NvbnRyb2xtZWNoYW5pc20u\ncHlxB1gIAAAAPGxhbWJkYT5xCE2cBEMAcQkpKXRxClJxC2Nwc3luZXVsaW5rLmNvcmUuY29tcG9u\nZW50cy5tZWNoYW5pc21zLm1vZHVsYXRvcnkuY29udHJvbC5jb250cm9sbWVjaGFuaXNtCl9fZGlj\ndF9fCmgITk59cQxOdHENUnEOLg==\n" + }, + "input_ports": { + "CONTROL_OUTCOME": { + "shape": "(1,)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, + "has_initializers": false, + "internal_only": true, + "variable": [ + 0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": null, + "weight": null, + "default_input": null + } + } + }, + "functions": { + "Default_Control_Function_0": { + "function": "defaultallocationfunction", + "args": { + "num_control_signals": 1, + "variable0": "CONTROL_OUTCOME" + }, + "metadata": { + "type": "DefaultAllocationFunction", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "variable": [ + [ + 1.0 + ] + ], + "execute_until_finished": true, + "enable_output_type_conversion": true, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "function_stateful_params": {} + } + } + }, + "output_ports": { + "CONTROL_TASK_gain__ControlSignal": { + "value": "Default_Control_Function_0", + "metadata": { + "type": "ControlSignal", + "require_projection_in_composition": true, + "has_initializers": false, + "variable": [ + 0.5 + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "modulation": "multiplicative_param", + "allocation_samples": null, + "projections": [ + [ + "TASK.input_ports.gain", + null, + null, + { + "PROJECTION_TYPE": "ControlProjection" + } + ] + ] + } + } + } + } + } + }, + "nodes": { + "color_input": { + "metadata": { + "type": "ProcessingMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null + }, + "input_ports": { + "color_input_InputPort_0": { + "shape": "(2,)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, + "has_initializers": false, + "internal_only": false, + "variable": [ + 0.0, + 0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": null, + "weight": null, + "default_input": null + } + } + }, + "functions": { + "Linear_Function_3": { + "function": "linear", + "args": { + "intercept": 0.0, + "slope": 1.0, + "variable0": "color_input_InputPort_0" + }, + "metadata": { + "type": "Linear", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "enable_output_type_conversion": true, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": null, + "function_stateful_params": {} + } + } + }, + "output_ports": { + "color_input_OutputPort_0": { + "value": "Linear_Function_3", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 0.0, + 0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } + } + } + }, + "color_hidden": { + "metadata": { + "type": "ProcessingMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 ] + ], + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null + }, + "input_ports": { + "color_hidden_input_port_MappingProjection_from_color_input_OutputPort_0__to_color_hidden_InputPort_0_": { + "shape": "(2,)", + "type": "float64" }, - "type": { - "PNL": "ObjectiveMechanism", - "generic": null + "color_hidden_input_port_MappingProjection_from_TASK_RESULT__to_color_hidden_InputPort_0_": { + "shape": "(2,)", + "type": "float64" } }, - "output_ports": [ - { - "MECHANISM": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "bias": { - "source": "TASK.input_ports.bias", - "type": "float", - "value": 0.0 - }, - "bounds": null, - "execute_until_finished": true, - "gain": { - "source": "TASK.input_ports.gain", - "type": "float", - "value": 1.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": { - "source": "TASK.input_ports.offset", - "type": "float", - "value": 0.0 - }, - "scale": { - "source": "TASK.input_ports.scale", - "type": "float", - "value": 1.0 - }, - "x_0": { - "source": "TASK.input_ports.x_0", - "type": "float", - "value": 0.0 - } - }, - "name": "Logistic Function-5", - "type": { - "generic": "Logistic" - } - } + "functions": { + "color_hidden_input_combination_function": { + "function": "onnx::ReduceSum", + "args": { + "data": "combination_function_input_data", + "axes": 0 + } + }, + "color_hidden_input_combination_function_dimreduce": { + "value": "variable0[0][0]", + "args": { + "variable0": "color_hidden_input_combination_function" + } + }, + "Logistic_Function_0": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": -4.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "color_hidden_input_combination_function_dimreduce" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, + "has_initializers": false, + "enable_output_type_conversion": true, + "variable": [ + [ + 0.0, + 0.0 + ] ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-65", - "type": { - "PNL": "LinearCombination", - "generic": null - } - } - ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-39", - "type": { - "generic": "Linear" - } - } - ], - "name": "scale", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-40", - "type": { - "generic": "Linear" - } - } - ], - "name": "offset", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-41", - "type": { - "generic": "Linear" - } - } - ], - "name": "x_0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-42", - "type": { - "generic": "Linear" - } - } - ], - "name": "gain", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-43", - "type": { - "generic": "Linear" - } - } - ], - "name": "bias", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-44", - "type": { - "generic": "Linear" - } - } - ], - "name": "integration_rate", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.1 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-45", - "type": { - "generic": "Linear" - } - } - ], - "name": "time_step_size", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.1 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-46", - "type": { - "generic": "Linear" - } - } - ], - "name": "competition", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-47", - "type": { - "generic": "Linear" - } - } - ], - "name": "smoothing_factor", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-48", - "type": { - "generic": "Linear" - } - } - ], - "name": "leak", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - -1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-49", - "type": { - "generic": "Linear" - } - } - ], - "name": "hetero", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - -1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-50", - "type": { - "generic": "Linear" - } - } - ], - "name": "noise", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-51", - "type": { - "generic": "Linear" - } - } - ], - "name": "auto", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - } - ], - "name": "TASK", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.6283161882953663, - 0.6283161882953663 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-54", - "type": { - "generic": "Linear" - } - } - ], - "name": "RESULT", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.6283161882953663, - 0.6283161882953663 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null - } - } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "auto": 0.0, - "clip": null, - "combination_function": "LinearCombination", - "competition": 1.0, - "enable_learning": false, - "execute_until_finished": true, - "has_integrated": false, - "has_recurrent_input_port": false, - "hetero": -1.0, - "initial_value": [ - [ - 0.5, - 0.5 - ] - ], - "input_ports": null, - "integration_rate": 0.5, - "integrator_function": "LeakyCompetingIntegrator", - "integrator_function_value": [ - [ - 0 - ] - ], - "integrator_mode": true, - "is_finished_flag": true, - "leak": 0.5, - "learning_condition": null, - "learning_function": "Hebbian", - "learning_rate": null, - "matrix": "HollowMatrix", - "max_executions_before_finished": 1000, - "noise": 0.0, - "num_executions_before_finished": 0, - "on_resume_integrator_mode": "instantaneous_mode_value", - "output_ports": [ - "RESULT" - ], - "smoothing_factor": 0.5, - "termination_comparison_op": ">=", - "termination_measure": "max", - "termination_measure_value": 0.0, - "termination_threshold": null, - "time_step_size": 0.1 - }, - "type": { - "PNL": "LCAMechanism", - "generic": null - } - }, - "name": "gain" - } - ], - "reconfiguration_cost": null, - "system": null - }, - "type": { - "PNL": "ControlMechanism", - "generic": null - } - }, - "Conflict Monitor": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "matrix": [ - [ - 0, - -2.5 - ], - [ - -2.5, - 0 - ] - ], - "max_executions_before_finished": 1000, - "metric": "energy", - "metric_fct": null, - "normalize": false, - "num_executions_before_finished": 0, - "transfer_fct": null - }, - "name": "Stability Function-0", - "type": { - "PNL": "Energy", - "generic": null - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-77", - "type": { - "PNL": "LinearCombination", - "generic": null - } - } - ], - "name": "Value of OUTPUT [OutputPort-0]", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": [ - [ - "OUTPUT.output_ports.OutputPort-0", - null, - null, - { - "PROJECTION_TYPE": "MappingProjection" - } - ] - ], - "weight": null - }, - "shape": "(1, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - } - ], - "name": "Conflict Monitor", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - -0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-71", - "type": { - "generic": "Linear" - } - } - ], - "name": "OUTCOME", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - -0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "OutputPort", - "generic": null - } - } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "input_ports": [ - { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "bias": { - "source": "OUTPUT.input_ports.bias", - "type": "float", - "value": 0.0 - }, - "bounds": null, - "execute_until_finished": true, - "gain": { - "source": "OUTPUT.input_ports.gain", - "type": "float", - "value": 1.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": { - "source": "OUTPUT.input_ports.offset", - "type": "float", - "value": 0.0 - }, - "scale": { - "source": "OUTPUT.input_ports.scale", - "type": "float", - "value": 1.0 - }, - "x_0": { - "source": "OUTPUT.input_ports.x_0", - "type": "float", - "value": 0.0 - } - }, - "name": "Logistic Function-3", - "type": { - "generic": "Logistic" - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-33", - "type": { - "PNL": "LinearCombination", - "generic": null - } - } - ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-15", - "type": { - "generic": "Linear" - } - } - ], - "name": "scale", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-16", - "type": { - "generic": "Linear" - } - } - ], - "name": "offset", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-17", - "type": { - "generic": "Linear" - } - } - ], - "name": "x_0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-18", - "type": { - "generic": "Linear" - } - } - ], - "name": "gain", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-19", - "type": { - "generic": "Linear" - } - } - ], - "name": "bias", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - } - ], - "name": "OUTPUT", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5, - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-21", - "type": { - "generic": "Linear" - } - } - ], - "name": "OutputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5, - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null - } - } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "input_ports": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": null - }, - "type": { - "PNL": "ProcessingMechanism", - "generic": null - } - } - ], - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": [ - "OUTCOME" - ] - }, - "type": { - "PNL": "ObjectiveMechanism", - "generic": null - } - }, - "DECISION": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0 - ] - ] - }, - "bias": 0.5, - "drift_rate": { - "source": "DECISION.input_ports.drift_rate", - "type": "float", - "value": 1.0 - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "noise": { - "source": "DECISION.input_ports.noise", - "type": "float", - "value": 0.5 - }, - "num_executions_before_finished": 0, - "starting_point": { - "source": "DECISION.input_ports.starting_point", - "type": "float", - "value": 0.0 - }, - "t0": 0.2, - "threshold": { - "source": "DECISION.input_ports.threshold", - "type": "float", - "value": 1.0 - } - }, - "name": "Drift Diffusion Analytical Function-10", - "type": { - "PNL": "DriftDiffusionAnalytical", - "generic": null - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": [ - 1, - -1 - ] - }, - "name": "Reduce Function-1", - "type": { - "PNL": "Reduce", - "generic": null - } - } - ], - "name": "ARRAY", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(1, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-58", - "type": { - "generic": "Linear" - } - } - ], - "name": "drift_rate", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-59", - "type": { - "generic": "Linear" - } - } - ], - "name": "threshold", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-60", - "type": { - "generic": "Linear" - } - } - ], - "name": "starting_point", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-61", - "type": { - "generic": "Linear" - } - } - ], - "name": "noise", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - } - ], - "name": "DECISION", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - -1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-65", - "type": { - "generic": "Linear" - } - } - ], - "name": "DECISION_VARIABLE", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - -1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "OutputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 4.2 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-67", - "type": { - "generic": "Linear" - } - } - ], - "name": "RESPONSE_TIME", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 4.2 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "OutputPort", - "generic": null - } - } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0 - ] - ] - }, - "execute_until_finished": false, - "initializer": [ - [ - 0 - ] - ], - "input_format": "SCALAR", - "input_ports": [ - { - "function": { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": [ - 1, - -1 - ] - }, - "name": "Reduce Function-1", - "type": { - "PNL": "Reduce", - "generic": null - } - }, - "name": "ARRAY", - "variable": [ - [ - 0.0, - 0.0 - ] - ] - } - ], - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": [ - "DECISION_VARIABLE", - "RESPONSE_TIME" - ], - "random_state": "numpy.random.RandomState()" - }, - "type": { - "PNL": "DDM", - "generic": null - } - }, - "OUTPUT": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "bias": { - "source": "OUTPUT.input_ports.bias", - "type": "float", - "value": 0.0 - }, - "bounds": null, - "execute_until_finished": true, - "gain": { - "source": "OUTPUT.input_ports.gain", - "type": "float", - "value": 1.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": { - "source": "OUTPUT.input_ports.offset", - "type": "float", - "value": 0.0 - }, - "scale": { - "source": "OUTPUT.input_ports.scale", - "type": "float", - "value": 1.0 - }, - "x_0": { - "source": "OUTPUT.input_ports.x_0", - "type": "float", - "value": 0.0 - } - }, - "name": "Logistic Function-3", - "type": { - "generic": "Logistic" - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-33", - "type": { - "PNL": "LinearCombination", - "generic": null - } - } - ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-15", - "type": { - "generic": "Linear" - } - } - ], - "name": "scale", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-16", - "type": { - "generic": "Linear" - } - } - ], - "name": "offset", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-17", - "type": { - "generic": "Linear" - } - } - ], - "name": "x_0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-18", - "type": { - "generic": "Linear" - } - } - ], - "name": "gain", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-19", - "type": { - "generic": "Linear" - } - } - ], - "name": "bias", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - } - ], - "name": "OUTPUT", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5, - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-21", - "type": { - "generic": "Linear" - } - } - ], - "name": "OutputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5, - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null - } - } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "input_ports": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": null - }, - "type": { - "PNL": "ProcessingMechanism", - "generic": null - } - }, - "TASK": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "bias": { - "source": "TASK.input_ports.bias", - "type": "float", - "value": 0.0 - }, - "bounds": null, - "execute_until_finished": true, - "gain": { - "source": "TASK.input_ports.gain", - "type": "float", - "value": 1.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": { - "source": "TASK.input_ports.offset", - "type": "float", - "value": 0.0 - }, - "scale": { - "source": "TASK.input_ports.scale", - "type": "float", - "value": 1.0 - }, - "x_0": { - "source": "TASK.input_ports.x_0", - "type": "float", - "value": 0.0 - } - }, - "name": "Logistic Function-5", - "type": { - "generic": "Logistic" - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-65", - "type": { - "PNL": "LinearCombination", - "generic": null - } - } - ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 0.0, - 0.0 - ] - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-39", - "type": { - "generic": "Linear" - } - } - ], - "name": "scale", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-40", - "type": { - "generic": "Linear" - } - } - ], - "name": "offset", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-41", - "type": { - "generic": "Linear" - } - } - ], - "name": "x_0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-42", - "type": { - "generic": "Linear" - } - } - ], - "name": "gain", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-43", - "type": { - "generic": "Linear" - } + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} } - ], - "name": "bias", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-44", - "type": { - "generic": "Linear" - } - } - ], - "name": "integration_rate", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + "parameters": { + "combination_function_input_data": { + "value": "[color_hidden_input_port_MappingProjection_from_color_input_OutputPort_0__to_color_hidden_InputPort_0_, color_hidden_input_port_MappingProjection_from_TASK_RESULT__to_color_hidden_InputPort_0_]" } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.1 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-45", - "type": { - "generic": "Linear" - } - } - ], - "name": "time_step_size", - "parameters": { - "PNL": { - "execution_count": 0, + "output_ports": { + "color_hidden_OutputPort_0": { + "value": "Logistic_Function_0", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, "has_initializers": false, - "require_projection_in_composition": true, "variable": [ - 0.1 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-46", - "type": { - "generic": "Linear" - } - } - ], - "name": "competition", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, + 0.017986209962091562, + 0.017986209962091562 + ], + "execute_until_finished": true, "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-47", - "type": { - "generic": "Linear" - } + "projections": null } + } + } + }, + "OUTPUT": { + "metadata": { + "type": "ProcessingMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] ], - "name": "smoothing_factor", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null + }, + "input_ports": { + "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "shape": "(2,)", + "type": "float64" }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + "OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "shape": "(2,)", + "type": "float64" } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.5 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-48", - "type": { - "generic": "Linear" - } + "functions": { + "OUTPUT_input_combination_function": { + "function": "onnx::ReduceSum", + "args": { + "data": "combination_function_input_data", + "axes": 0 } - ], - "name": "leak", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.5 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - -1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-49", - "type": { - "generic": "Linear" - } + "OUTPUT_input_combination_function_dimreduce": { + "value": "variable0[0][0]", + "args": { + "variable0": "OUTPUT_input_combination_function" } - ], - "name": "hetero", - "parameters": { - "PNL": { - "execution_count": 0, + }, + "Logistic_Function_1": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": 0.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "OUTPUT_input_combination_function_dimreduce" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, "has_initializers": false, - "require_projection_in_composition": true, + "enable_output_type_conversion": true, "variable": [ - -1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} + } } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-50", - "type": { - "generic": "Linear" - } - } - ], - "name": "noise", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + "parameters": { + "combination_function_input_data": { + "value": "[OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_, OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_]" } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-51", - "type": { - "generic": "Linear" - } - } - ], - "name": "auto", - "parameters": { - "PNL": { - "execution_count": 0, + "output_ports": { + "OUTPUT_OutputPort_0": { + "value": "Logistic_Function_1", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, "has_initializers": false, - "require_projection_in_composition": true, "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - } - ], - "name": "TASK", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.6283161882953663, - 0.6283161882953663 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-54", - "type": { - "generic": "Linear" - } - } - ], - "name": "RESULT", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, + 0.5, + 0.5 + ], + "execute_until_finished": true, "require_projection_in_composition": true, - "variable": [ - 0.6283161882953663, - 0.6283161882953663 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null + "projections": null + } } } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, + }, + "word_input": { + "metadata": { + "type": "ProcessingMechanism", "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, + "max_executions_before_finished": 1000, + "has_initializers": false, "variable": [ [ 0.0, 0.0 ] - ] + ], + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null }, - "auto": 0.0, - "clip": null, - "combination_function": "LinearCombination", - "competition": 1.0, - "enable_learning": false, - "execute_until_finished": true, - "has_integrated": false, - "has_recurrent_input_port": false, - "hetero": -1.0, - "initial_value": [ - [ - 0.5, - 0.5 - ] - ], - "input_ports": null, - "integration_rate": 0.5, - "integrator_function": "LeakyCompetingIntegrator", - "integrator_function_value": [ - [ - 0 - ] - ], - "integrator_mode": true, - "is_finished_flag": true, - "leak": 0.5, - "learning_condition": null, - "learning_function": "Hebbian", - "learning_rate": null, - "matrix": "HollowMatrix", - "max_executions_before_finished": 1000, - "noise": 0.0, - "num_executions_before_finished": 0, - "on_resume_integrator_mode": "instantaneous_mode_value", - "output_ports": [ - "RESULT" - ], - "smoothing_factor": 0.5, - "termination_comparison_op": ">=", - "termination_measure": "max", - "termination_measure_value": 0.0, - "termination_threshold": null, - "time_step_size": 0.1 - }, - "type": { - "PNL": "LCAMechanism", - "generic": null - } - }, - "color_hidden": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, + "input_ports": { + "word_input_InputPort_0": { + "shape": "(2,)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, + "has_initializers": false, + "internal_only": false, + "variable": [ + 0.0, + 0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": null, + "weight": null, + "default_input": null + } + } + }, + "functions": { + "Linear_Function_22": { + "function": "linear", + "args": { + "intercept": 0.0, + "slope": 1.0, + "variable0": "word_input_InputPort_0" + }, + "metadata": { + "type": "Linear", + "max_executions_before_finished": 1000, + "changes_shape": false, "has_initializers": false, - "output_type": 3, "variable": [ [ 0.0, 0.0 ] - ] - }, - "bias": { - "source": "color_hidden.input_ports.bias", - "type": "int", - "value": -4 - }, - "bounds": null, - "execute_until_finished": true, - "gain": { - "source": "color_hidden.input_ports.gain", - "type": "float", - "value": 1.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": { - "source": "color_hidden.input_ports.offset", - "type": "float", - "value": 0.0 - }, - "scale": { - "source": "color_hidden.input_ports.scale", - "type": "float", - "value": 1.0 - }, - "x_0": { - "source": "color_hidden.input_ports.x_0", - "type": "float", - "value": 0.0 + ], + "execute_until_finished": true, + "enable_output_type_conversion": true, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": null, + "function_stateful_params": {} } - }, - "name": "Logistic Function-2", - "type": { - "generic": "Logistic" } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 2.513264753181465, - 2.513264753181465 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-25", - "type": { - "PNL": "LinearCombination", - "generic": null - } + }, + "output_ports": { + "word_input_OutputPort_0": { + "value": "Linear_Function_22", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 0.0, + 0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null } + } + } + }, + "word_hidden": { + "metadata": { + "type": "ProcessingMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null + }, + "input_ports": { + "word_hidden_input_port_MappingProjection_from_word_input_OutputPort_0__to_word_hidden_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + }, + "word_hidden_input_port_MappingProjection_from_TASK_RESULT__to_word_hidden_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + } + }, + "functions": { + "word_hidden_input_combination_function": { + "function": "onnx::ReduceSum", + "args": { + "data": "combination_function_input_data", + "axes": 0 + } + }, + "word_hidden_input_combination_function_dimreduce": { + "value": "variable0[0][0]", + "args": { + "variable0": "word_hidden_input_combination_function" + } + }, + "Logistic_Function_2": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": -4.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "word_hidden_input_combination_function_dimreduce" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, + "enable_output_type_conversion": true, "variable": [ [ 0.0, 0.0 - ], - [ - 2.513264753181465, - 2.513264753181465 ] - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2, 2)", - "type": { - "PNL": "InputPort", - "generic": null + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} + } } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-8", - "type": { - "generic": "Linear" - } - } - ], - "name": "scale", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + "parameters": { + "combination_function_input_data": { + "value": "[word_hidden_input_port_MappingProjection_from_word_input_OutputPort_0__to_word_hidden_InputPort_0_, word_hidden_input_port_MappingProjection_from_TASK_RESULT__to_word_hidden_InputPort_0_]" } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-9", - "type": { - "generic": "Linear" - } + "output_ports": { + "word_hidden_OutputPort_0": { + "value": "Logistic_Function_2", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 0.017986209962091562, + 0.017986209962091562 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null } + } + } + }, + "task_input": { + "metadata": { + "type": "ProcessingMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] ], - "name": "offset", - "parameters": { - "PNL": { - "execution_count": 0, + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null + }, + "input_ports": { + "task_input_InputPort_0": { + "shape": "(2,)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, "has_initializers": false, - "require_projection_in_composition": true, + "internal_only": false, "variable": [ + 0.0, 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": null, + "weight": null, + "default_input": null + } } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-10", - "type": { - "generic": "Linear" - } - } - ], - "name": "x_0", - "parameters": { - "PNL": { - "execution_count": 0, + "functions": { + "Linear_Function_34": { + "function": "linear", + "args": { + "intercept": 0.0, + "slope": 1.0, + "variable0": "task_input_InputPort_0" + }, + "metadata": { + "type": "Linear", + "max_executions_before_finished": 1000, + "changes_shape": false, "has_initializers": false, - "require_projection_in_composition": true, "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "enable_output_type_conversion": true, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": null, + "function_stateful_params": {} + } } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-11", - "type": { - "generic": "Linear" - } - } - ], - "name": "gain", - "parameters": { - "PNL": { - "execution_count": 0, + "output_ports": { + "task_input_OutputPort_0": { + "value": "Linear_Function_34", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, "has_initializers": false, - "require_projection_in_composition": true, "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + 0.0, + 0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } } - }, - { - "dtype": "int64", - "functions": [ - { + } + }, + "TASK": { + "metadata": { + "type": "LCAMechanism", + "output_labels_dict": {}, + "input_port_variables": null, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "input_labels_dict": {}, + "max_executions_before_finished": 1000, + "termination_measure_value": 0.0, + "integrator_function_value": [ + [ + 0 + ] + ], + "output_ports": [ + "RESULTS" + ], + "termination_threshold": null, + "clip": null, + "has_recurrent_input_port": false, + "termination_measure": "max", + "input_ports": null, + "matrix": "InverseHollowMatrix", + "termination_comparison_op": ">=", + "integrator_mode": true, + "combination_function": { + "LinearCombination_Function_23": { + "function": "linearcombination", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - -4 + "offset": 0.0, + "scale": 1.0 + }, + "metadata": { + "type": "LinearCombination", + "changes_shape": false, + "has_initializers": false, + "enable_output_type_conversion": false, + "variable": [ + [ + 0.0, + 0.0 ] - }, - "bounds": null, + ], "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-12", - "type": { - "generic": "Linear" + "output_type": "FunctionOutputType.DEFAULT", + "weights": null, + "operation": "sum", + "exponents": null, + "function_stateful_params": {} } } - ], - "name": "bias", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - -4 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - } - ], - "name": "color_hidden", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { + "on_resume_integrator_mode": "current_value", + "enable_learning": false, + "integrator_function": { + "LeakyCompetingIntegrator_Function_0": { + "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.017986209962091562, - 0.017986209962091562 + "rate": 0.5, + "time_step_size": 0.1, + "offset": 0.0, + "noise": 0.0 + }, + "metadata": { + "type": "LeakyCompetingIntegrator", + "initializer": [ + [ + 0.5, + 0.5 ] - }, - "bounds": null, + ], + "changes_shape": false, + "has_initializers": true, + "enable_output_type_conversion": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-14", - "type": { - "generic": "Linear" + "output_type": "FunctionOutputType.DEFAULT", + "function_stateful_params": { + "previous_value": { + "id": "previous_value", + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } + } } } - ], - "name": "OutputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, + } + }, + "input_ports": { + "TASK_input_port_TASK_recurrent_projection": { + "shape": "(2,)", + "type": "float64" + }, + "TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + } + }, + "functions": { + "TASK_input_combination_function": { + "function": "onnx::ReduceSum", + "args": { + "data": "combination_function_input_data", + "axes": 0 + } + }, + "TASK_input_combination_function_dimreduce": { + "value": "variable0[0][0]", + "args": { + "variable0": "TASK_input_combination_function" + } + }, + "Logistic_Function_5": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": 0.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "LeakyCompetingIntegrator_Function_0" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, "has_initializers": false, - "require_projection_in_composition": true, + "enable_output_type_conversion": true, "variable": [ - 0.017986209962091562, - 0.017986209962091562 - ] + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} + } + }, + "LeakyCompetingIntegrator_Function_0": { + "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", + "args": { + "rate": 0.5, + "time_step_size": 0.1, + "offset": 0.0, + "noise": 0.0, + "variable0": "TASK_input_combination_function_dimreduce" }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null + "metadata": { + "type": "LeakyCompetingIntegrator", + "initializer": [ + [ + 0.5, + 0.5 + ] + ], + "changes_shape": false, + "has_initializers": true, + "enable_output_type_conversion": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.DEFAULT", + "function_stateful_params": { + "previous_value": { + "id": "previous_value", + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } + } + } + } + }, + "parameters": { + "auto": { + "value": 0.0 }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null + "competition": { + "value": 1.0 + }, + "smoothing_factor": { + "value": 0.5 + }, + "hetero": { + "value": -1.0 + }, + "combination_function_input_data": { + "value": "[TASK_input_port_TASK_recurrent_projection, TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_]" + }, + "previous_value": { + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } + }, + "output_ports": { + "TASK_RESULT": { + "value": "Logistic_Function_5", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } } } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, + }, + "DECISION": { + "metadata": { + "type": "DDM", "output_labels_dict": {}, - "previous_value": null, + "input_port_variables": null, + "has_initializers": false, "variable": [ [ - 0.0, 0.0 ] - ] - }, - "execute_until_finished": true, - "input_ports": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": null - }, - "type": { - "PNL": "ProcessingMechanism", - "generic": null - } - }, - "color_input": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, + ], + "execute_until_finished": true, + "input_labels_dict": {}, + "max_executions_before_finished": 1000, + "output_ports": [ + "DECISION_VARIABLE", + "RESPONSE_TIME" + ], + "input_ports": [ + { + "name": "ARRAY", "variable": [ [ 0.0, 0.0 ] - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": { - "source": "color_input.input_ports.intercept", - "type": "float", - "value": 0.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": { - "source": "color_input.input_ports.slope", - "type": "float", - "value": 1.0 - } - }, - "name": "Linear Function-5", - "type": { - "generic": "Linear" - } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0, - 0.0 - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-17", - "type": { - "PNL": "LinearCombination", - "generic": null + ], + "function": { + "Reduce_Function_0_2": { + "function": "reduce", + "args": { + "offset": 0.0, + "scale": 1.0 + }, + "metadata": { + "type": "Reduce", + "changes_shape": true, + "has_initializers": false, + "enable_output_type_conversion": false, + "variable": [ + 0 + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.DEFAULT", + "weights": [ + 1, + -1 + ], + "operation": "sum", + "exponents": null, + "function_stateful_params": {} + } + } } } ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, + "input_format": "SCALAR", + "random_state": null + }, + "input_ports": { + "DECISION_ARRAY": { + "shape": "(1, 2)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, "has_initializers": false, "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, "variable": [ - 0.0, - 0.0 - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2,)", - "type": { - "PNL": "InputPort", - "generic": null + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": null, + "weight": null, + "default_input": null + } } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-3", - "type": { - "generic": "Linear" - } - } - ], - "name": "intercept", - "parameters": { - "PNL": { - "execution_count": 0, + "functions": { + "Drift_Diffusion_Analytical_Function_0_3": { + "function": "driftdiffusionanalytical", + "args": { + "bias": 0.5, + "starting_value": 0.0, + "threshold": 1.0, + "drift_rate": 1.0, + "non_decision_time": 0.2, + "noise": 0.5, + "shape": [ + 1, + 1 + ], + "variable0": "DECISION_ARRAY" + }, + "metadata": { + "type": "DriftDiffusionAnalytical", + "changes_shape": false, "has_initializers": false, - "require_projection_in_composition": true, + "enable_output_type_conversion": true, "variable": [ - 0.0 + [ + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "function_stateful_params": {} + } + } + }, + "parameters": { + "initializer": { + "value": [ + [ + 0 ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null + ] }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + "seed": { + "value": -1 } }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-4", - "type": { - "generic": "Linear" - } - } - ], - "name": "slope", - "parameters": { - "PNL": { - "execution_count": 0, + "output_ports": { + "DECISION_DECISION_VARIABLE": { + "value": "Drift_Diffusion_Analytical_Function_0_3[0]", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, "has_initializers": false, - "require_projection_in_composition": true, "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null + -1.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null + "DECISION_RESPONSE_TIME": { + "value": "Drift_Diffusion_Analytical_Function_0_3[1]", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 4.2 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } } } - ], - "name": "color_input", - "output_ports": [ - { - "dtype": "float64", - "functions": [ + }, + "Conflict_Monitor": { + "metadata": { + "type": "ObjectiveMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": [ { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, + "OUTPUT": { + "metadata": { + "type": "ProcessingMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, "has_initializers": false, - "output_type": 3, "variable": [ - 0.0, - 0.0 - ] + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-7", - "type": { - "generic": "Linear" + "input_ports": { + "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + }, + "OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + } + }, + "functions": { + "OUTPUT_input_combination_function": { + "function": "onnx::ReduceSum", + "args": { + "data": "combination_function_input_data", + "axes": 0 + } + }, + "OUTPUT_input_combination_function_dimreduce": { + "value": "variable0[0][0]", + "args": { + "variable0": "OUTPUT_input_combination_function" + } + }, + "Logistic_Function_1": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": 0.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "OUTPUT_input_combination_function_dimreduce" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, + "has_initializers": false, + "enable_output_type_conversion": true, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} + } + } + }, + "parameters": { + "combination_function_input_data": { + "value": "[OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_, OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_]" + } + }, + "output_ports": { + "OUTPUT_OutputPort_0": { + "value": "Logistic_Function_1", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } + } + } } } ], - "name": "OutputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, + "output_ports": [ + "OUTCOME" + ] + }, + "input_ports": { + "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_": { + "shape": "(1, 2)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, "has_initializers": false, - "require_projection_in_composition": true, + "internal_only": true, "variable": [ - 0.0, - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": [ + [ + "OUTPUT.output_ports.OutputPort-0", + null, + null, + { + "PROJECTION_TYPE": "MappingProjection" + } + ] + ], + "weight": null, + "default_input": null + } } - } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] }, - "execute_until_finished": true, - "input_ports": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": null - }, - "type": { - "PNL": "ProcessingMechanism", - "generic": null - } - }, - "task_input": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, + "functions": { + "Stability_Function_0": { + "function": "energy", + "args": { + "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" + }, + "metadata": { + "type": "Energy", + "max_executions_before_finished": 1000, + "changes_shape": false, "has_initializers": false, - "output_type": 3, "variable": [ [ 0.0, 0.0 ] - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": { - "source": "task_input.input_ports.intercept", - "type": "float", - "value": 0.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": { - "source": "task_input.input_ports.slope", - "type": "float", - "value": 1.0 + ], + "execute_until_finished": true, + "enable_output_type_conversion": true, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "matrix": [ + [ + 0, + -2.5 + ], + [ + -2.5, + 0 + ] + ], + "metric": "energy", + "transfer_fct": null, + "metric_fct": { + "Distance_Function_4": { + "function": "distance", + "args": {}, + "metadata": { + "type": "Distance", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "variable": [ + [ + [ + 0.0, + 0.0 + ] + ], + [ + [ + 0.0, + 0.0 + ] + ] + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "metric": "energy", + "normalize": false, + "function_stateful_params": {} + } + } + }, + "normalize": false, + "function_stateful_params": {} + } + } + }, + "output_ports": { + "Conflict_Monitor_OUTCOME": { + "value": "Stability_Function_0", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + -0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null } - }, - "name": "Linear Function-36", - "type": { - "generic": "Linear" } } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ + }, + "CONTROL": { + "metadata": { + "type": "ControlMechanism", + "output_labels_dict": {}, + "modulation": "multiplicative_param", + "input_port_variables": null, + "has_initializers": false, + "outcome": null, + "variable": [ + [ + 1.0 + ] + ], + "execute_until_finished": true, + "input_labels_dict": {}, + "simulation_ids": [], + "max_executions_before_finished": 1000, + "control_signal_costs": null, + "net_outcome": null, + "reconfiguration_cost": null, + "objective_mechanism": { + "Conflict_Monitor": { + "metadata": { + "type": "ObjectiveMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ 0.0, 0.0 ] - }, + ], "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": [ + { + "OUTPUT": { + "metadata": { + "type": "ProcessingMechanism", + "input_port_variables": null, + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "output_labels_dict": {}, + "input_labels_dict": {}, + "input_ports": null, + "output_ports": null + }, + "input_ports": { + "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + }, + "OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + } + }, + "functions": { + "OUTPUT_input_combination_function": { + "function": "onnx::ReduceSum", + "args": { + "data": "combination_function_input_data", + "axes": 0 + } + }, + "OUTPUT_input_combination_function_dimreduce": { + "value": "variable0[0][0]", + "args": { + "variable0": "OUTPUT_input_combination_function" + } + }, + "Logistic_Function_1": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": 0.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "OUTPUT_input_combination_function_dimreduce" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, + "has_initializers": false, + "enable_output_type_conversion": true, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} + } + } + }, + "parameters": { + "combination_function_input_data": { + "value": "[OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_, OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_]" + } + }, + "output_ports": { + "OUTPUT_OutputPort_0": { + "value": "Logistic_Function_1", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } + } + } + } + } + ], + "output_ports": [ + "OUTCOME" + ] + }, + "input_ports": { + "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_": { + "shape": "(1, 2)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, + "has_initializers": false, + "internal_only": true, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": [ + [ + "OUTPUT.output_ports.OutputPort-0", + null, + null, + { + "PROJECTION_TYPE": "MappingProjection" + } + ] + ], + "weight": null, + "default_input": null + } + } }, - "name": "LinearCombination Function-57", - "type": { - "PNL": "LinearCombination", - "generic": null - } - } - ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - 0.0, - 0.0 - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2,)", - "type": { - "PNL": "InputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 + "functions": { + "Stability_Function_0": { + "function": "energy", + "args": { + "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" + }, + "metadata": { + "type": "Energy", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "enable_output_type_conversion": true, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "matrix": [ + [ + 0, + -2.5 + ], + [ + -2.5, + 0 + ] + ], + "metric": "energy", + "transfer_fct": null, + "metric_fct": { + "Distance_Function_4": { + "function": "distance", + "args": {}, + "metadata": { + "type": "Distance", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "variable": [ + [ + [ + 0.0, + 0.0 + ] + ], + [ + [ + 0.0, + 0.0 + ] + ] + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "metric": "energy", + "normalize": false, + "function_stateful_params": {} + } + } + }, + "normalize": false, + "function_stateful_params": {} + } + } }, - "name": "Linear Function-34", - "type": { - "generic": "Linear" + "output_ports": { + "Conflict_Monitor_OUTCOME": { + "value": "Stability_Function_0", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + -0.0 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } + } } } - ], - "name": "intercept", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-35", - "type": { - "generic": "Linear" - } - } + "outcome_input_ports_option": "separate", + "default_allocation": [ + 0.5 ], - "name": "slope", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - } - ], - "name": "task_input", - "output_ports": [ - { - "dtype": "float64", - "functions": [ + "output_ports": [ { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0, - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-38", - "type": { - "generic": "Linear" + "name": "gain", + "MECHANISM": { + "TASK": { + "metadata": { + "type": "LCAMechanism", + "output_labels_dict": {}, + "input_port_variables": null, + "has_initializers": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "input_labels_dict": {}, + "max_executions_before_finished": 1000, + "termination_measure_value": 0.0, + "integrator_function_value": [ + [ + 0 + ] + ], + "output_ports": [ + "RESULTS" + ], + "termination_threshold": null, + "clip": null, + "has_recurrent_input_port": false, + "termination_measure": "max", + "input_ports": null, + "matrix": "InverseHollowMatrix", + "termination_comparison_op": ">=", + "integrator_mode": true, + "combination_function": { + "LinearCombination_Function_23": { + "function": "linearcombination", + "args": { + "offset": 0.0, + "scale": 1.0 + }, + "metadata": { + "type": "LinearCombination", + "changes_shape": false, + "has_initializers": false, + "enable_output_type_conversion": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.DEFAULT", + "weights": null, + "operation": "sum", + "exponents": null, + "function_stateful_params": {} + } + } + }, + "on_resume_integrator_mode": "current_value", + "enable_learning": false, + "integrator_function": { + "LeakyCompetingIntegrator_Function_0": { + "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", + "args": { + "rate": 0.5, + "time_step_size": 0.1, + "offset": 0.0, + "noise": 0.0 + }, + "metadata": { + "type": "LeakyCompetingIntegrator", + "initializer": [ + [ + 0.5, + 0.5 + ] + ], + "changes_shape": false, + "has_initializers": true, + "enable_output_type_conversion": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.DEFAULT", + "function_stateful_params": { + "previous_value": { + "id": "previous_value", + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } + } + } + } + } + }, + "input_ports": { + "TASK_input_port_TASK_recurrent_projection": { + "shape": "(2,)", + "type": "float64" + }, + "TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_": { + "shape": "(2,)", + "type": "float64" + } + }, + "functions": { + "TASK_input_combination_function": { + "function": "onnx::ReduceSum", + "args": { + "data": "combination_function_input_data", + "axes": 0 + } + }, + "TASK_input_combination_function_dimreduce": { + "value": "variable0[0][0]", + "args": { + "variable0": "TASK_input_combination_function" + } + }, + "Logistic_Function_5": { + "function": "logistic", + "args": { + "x_0": 0, + "bias": 0.0, + "gain": 1.0, + "offset": 0.0, + "scale": 1.0, + "variable0": "LeakyCompetingIntegrator_Function_0" + }, + "metadata": { + "type": "Logistic", + "changes_shape": false, + "has_initializers": false, + "enable_output_type_conversion": true, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "bounds": [ + 0, + 1 + ], + "function_stateful_params": {} + } + }, + "LeakyCompetingIntegrator_Function_0": { + "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", + "args": { + "rate": 0.5, + "time_step_size": 0.1, + "offset": 0.0, + "noise": 0.0, + "variable0": "TASK_input_combination_function_dimreduce" + }, + "metadata": { + "type": "LeakyCompetingIntegrator", + "initializer": [ + [ + 0.5, + 0.5 + ] + ], + "changes_shape": false, + "has_initializers": true, + "enable_output_type_conversion": false, + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "output_type": "FunctionOutputType.DEFAULT", + "function_stateful_params": { + "previous_value": { + "id": "previous_value", + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } + } + } + } + }, + "parameters": { + "auto": { + "value": 0.0 + }, + "competition": { + "value": 1.0 + }, + "smoothing_factor": { + "value": 0.5 + }, + "hetero": { + "value": -1.0 + }, + "combination_function_input_data": { + "value": "[TASK_input_port_TASK_recurrent_projection, TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_]" + }, + "previous_value": { + "default_initial_value": [ + [ + 0.5, + 0.5 + ] + ], + "value": "LeakyCompetingIntegrator_Function_0" + } + }, + "output_ports": { + "TASK_RESULT": { + "value": "Logistic_Function_5", + "metadata": { + "type": "OutputPort", + "max_executions_before_finished": 1000, + "has_initializers": false, + "variable": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "projections": null + } + } + } + } } } ], - "name": "OutputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, + "costs": null, + "compute_reconfiguration_cost": null, + "combine_costs": "gANjbnVtcHkKc3VtCnEALg==\n", + "input_ports": [ + "OUTCOME" + ], + "outcome_input_ports": "[(InputPort OUTCOME)]", + "monitor_for_control": [], + "compute_net_outcome": "gANjZGlsbC5fZGlsbApfY3JlYXRlX2Z1bmN0aW9uCnEAKGNkaWxsLl9kaWxsCl9jcmVhdGVfY29k\nZQpxAShLAksASwJLAktDQwh8AHwBGABTAHECToVxAylYBwAAAG91dGNvbWVxBFgEAAAAY29zdHEF\nhnEGWGwAAAAvaG9tZS9rYXRoZXJpbmUvY29kZS9Qc3lOZXVMaW5rL3BzeW5ldWxpbmsvY29yZS9j\nb21wb25lbnRzL21lY2hhbmlzbXMvbW9kdWxhdG9yeS9jb250cm9sL2NvbnRyb2xtZWNoYW5pc20u\ncHlxB1gIAAAAPGxhbWJkYT5xCE2cBEMAcQkpKXRxClJxC2Nwc3luZXVsaW5rLmNvcmUuY29tcG9u\nZW50cy5tZWNoYW5pc21zLm1vZHVsYXRvcnkuY29udHJvbC5jb250cm9sbWVjaGFuaXNtCl9fZGlj\ndF9fCmgITk59cQxOdHENUnEOLg==\n" + }, + "input_ports": { + "CONTROL_OUTCOME": { + "shape": "(1,)", + "type": "float64", + "metadata": { + "type": "InputPort", + "max_executions_before_finished": 1000, + "shadow_inputs": null, "has_initializers": false, - "require_projection_in_composition": true, + "internal_only": true, "variable": [ - 0.0, 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null + ], + "execute_until_finished": true, + "require_projection_in_composition": true, + "combine": null, + "exponent": null, + "projections": null, + "weight": null, + "default_input": null + } } - } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] }, - "execute_until_finished": true, - "input_ports": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": null - }, - "type": { - "PNL": "ProcessingMechanism", - "generic": null - } - }, - "word_hidden": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, + "functions": { + "Default_Control_Function_0": { + "function": "defaultallocationfunction", + "args": { + "num_control_signals": 1, + "variable0": "CONTROL_OUTCOME" + }, + "metadata": { + "type": "DefaultAllocationFunction", + "max_executions_before_finished": 1000, + "changes_shape": false, "has_initializers": false, - "output_type": 3, "variable": [ [ - 0.0, - 0.0 + 1.0 ] - ] - }, - "bias": { - "source": "word_hidden.input_ports.bias", - "type": "int", - "value": -4 - }, - "bounds": null, - "execute_until_finished": true, - "gain": { - "source": "word_hidden.input_ports.gain", - "type": "float", - "value": 1.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": { - "source": "word_hidden.input_ports.offset", - "type": "float", - "value": 0.0 - }, - "scale": { - "source": "word_hidden.input_ports.scale", - "type": "float", - "value": 1.0 - }, - "x_0": { - "source": "word_hidden.input_ports.x_0", - "type": "float", - "value": 0.0 + ], + "execute_until_finished": true, + "enable_output_type_conversion": true, + "output_type": "FunctionOutputType.NP_2D_ARRAY", + "function_stateful_params": {} } - }, - "name": "Logistic Function-4", - "type": { - "generic": "Logistic" } - } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ - 0.0, - 0.0 - ], - [ - 2.513264753181465, - 2.513264753181465 - ] - ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null - }, - "name": "LinearCombination Function-49", - "type": { - "PNL": "LinearCombination", - "generic": null - } - } - ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, + }, + "output_ports": { + "CONTROL_TASK_gain__ControlSignal": { + "value": "Default_Control_Function_0", + "metadata": { + "type": "ControlSignal", "require_projection_in_composition": true, - "shadow_inputs": null, + "has_initializers": false, "variable": [ + 0.5 + ], + "execute_until_finished": true, + "max_executions_before_finished": 1000, + "modulation": "multiplicative_param", + "allocation_samples": null, + "projections": [ [ - 0.0, - 0.0 - ], - [ - 2.513264753181465, - 2.513264753181465 + "TASK.input_ports.gain", + null, + null, + { + "PROJECTION_TYPE": "ControlProjection" + } ] ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2, 2)", - "type": { - "PNL": "InputPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-27", - "type": { - "generic": "Linear" - } } - ], - "name": "scale", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null } + } + } + }, + "edges": { + "MappingProjection_from_color_input_OutputPort_0__to_color_hidden_InputPort_0_": { + "parameters": { + "weight": 1 }, - { - "dtype": "float64", - "functions": [ - { + "sender": "color_input", + "receiver": "color_hidden", + "sender_port": "color_input_OutputPort_0", + "receiver_port": "color_hidden_input_port_MappingProjection_from_color_input_OutputPort_0__to_color_hidden_InputPort_0_", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_3": { + "function": "onnx::MatMul", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 + "B": [ + [ + 2.0, + -2.0 + ], + [ + -2.0, + 2.0 ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 + ] }, - "name": "Linear Function-28", - "type": { - "generic": "Linear" - } - } - ], - "name": "offset", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null - } - }, - { - "dtype": "float64", - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, + "metadata": { + "type": "LinearMatrix", "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 - }, - "name": "Linear Function-29", - "type": { - "generic": "Linear" + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.0, + 0.0 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } - ], - "name": "x_0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null } + } + }, + "MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "parameters": { + "weight": 1 }, - { - "dtype": "float64", - "functions": [ - { + "sender": "color_hidden", + "receiver": "OUTPUT", + "sender_port": "color_hidden_OutputPort_0", + "receiver_port": "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_4": { + "function": "onnx::MatMul", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 1.0 + "B": [ + [ + 2.0, + -2.0 + ], + [ + -2.0, + 2.0 ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 + ] }, - "name": "Linear Function-30", - "type": { - "generic": "Linear" + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.017986209962091562, + 0.017986209962091562 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } - ], - "name": "gain", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null } + } + }, + "MappingProjection_from_word_input_OutputPort_0__to_word_hidden_InputPort_0_": { + "parameters": { + "weight": 1 }, - { - "dtype": "int64", - "functions": [ - { + "sender": "word_input", + "receiver": "word_hidden", + "sender_port": "word_input_OutputPort_0", + "receiver_port": "word_hidden_input_port_MappingProjection_from_word_input_OutputPort_0__to_word_hidden_InputPort_0_", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_7": { + "function": "onnx::MatMul", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - -4 + "B": [ + [ + 3.0, + -3.0 + ], + [ + -3.0, + 3.0 ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 + ] }, - "name": "Linear Function-31", - "type": { - "generic": "Linear" + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.0, + 0.0 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } - ], - "name": "bias", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - -4 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null } } - ], - "name": "word_hidden", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { + }, + "MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { + "parameters": { + "weight": 1 + }, + "sender": "word_hidden", + "receiver": "OUTPUT", + "sender_port": "word_hidden_OutputPort_0", + "receiver_port": "OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_8": { + "function": "onnx::MatMul", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.017986209962091562, - 0.017986209962091562 - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 + "B": [ + [ + 3.0, + -3.0 + ], + [ + -3.0, + 3.0 + ] + ] }, - "name": "Linear Function-33", - "type": { - "generic": "Linear" + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.017986209962091562, + 0.017986209962091562 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } - ], - "name": "OutputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.017986209962091562, - 0.017986209962091562 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null } } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "input_ports": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": null }, - "type": { - "PNL": "ProcessingMechanism", - "generic": null - } - }, - "word_input": { - "functions": [ - { - "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - [ + "MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_": { + "parameters": { + "weight": 1 + }, + "sender": "task_input", + "receiver": "TASK", + "sender_port": "task_input_OutputPort_0", + "receiver_port": "TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_10": { + "function": "onnx::MatMul", + "args": { + "B": [ + [ + 1.0, + 0.0 + ], + [ + 0.0, + 1.0 + ] + ] + }, + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ 0.0, 0.0 - ] - ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": { - "source": "word_input.input_ports.intercept", - "type": "float", - "value": 0.0 - }, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": { - "source": "word_input.input_ports.slope", - "type": "float", - "value": 1.0 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} + } } - }, - "name": "Linear Function-24", - "type": { - "generic": "Linear" } } - ], - "input_ports": [ - { - "dtype": "float64", - "functions": [ - { + }, + "MappingProjection_from_TASK_RESULT__to_color_hidden_InputPort_0_": { + "parameters": { + "weight": 1 + }, + "sender": "TASK", + "receiver": "color_hidden", + "sender_port": "TASK_RESULT", + "receiver_port": "color_hidden_input_port_MappingProjection_from_TASK_RESULT__to_color_hidden_InputPort_0_", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_11": { + "function": "onnx::MatMul", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ + "B": [ + [ + 4.0, + 4.0 + ], + [ 0.0, 0.0 ] - }, - "execute_until_finished": true, - "exponents": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "offset": 0.0, - "operation": "sum", - "scale": 1.0, - "weights": null + ] }, - "name": "LinearCombination Function-41", - "type": { - "PNL": "LinearCombination", - "generic": null + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } - ], - "name": "InputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "internal_only": false, - "require_projection_in_composition": true, - "shadow_inputs": null, - "variable": [ - 0.0, - 0.0 - ] - }, - "combine": null, - "execute_until_finished": true, - "exponent": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null, - "weight": null - }, - "shape": "(2,)", - "type": { - "PNL": "InputPort", - "generic": null } + } + }, + "MappingProjection_from_TASK_RESULT__to_word_hidden_InputPort_0_": { + "parameters": { + "weight": 1 }, - { - "dtype": "float64", - "functions": [ - { + "sender": "TASK", + "receiver": "word_hidden", + "sender_port": "TASK_RESULT", + "receiver_port": "word_hidden_input_port_MappingProjection_from_TASK_RESULT__to_word_hidden_InputPort_0_", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_13": { + "function": "onnx::MatMul", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ + "B": [ + [ + 0.0, 0.0 + ], + [ + 4.0, + 4.0 ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 + ] }, - "name": "Linear Function-22", - "type": { - "generic": "Linear" + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } - ], - "name": "intercept", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null } + } + }, + "MappingProjection_from_OUTPUT_OutputPort_0__to_DECISION_ARRAY_": { + "parameters": { + "weight": 1 }, - { - "dtype": "float64", - "functions": [ - { + "sender": "OUTPUT", + "receiver": "DECISION", + "sender_port": "OUTPUT_OutputPort_0", + "receiver_port": "DECISION_ARRAY", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_14": { + "function": "onnx::MatMul", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ + "B": [ + [ + 1.0, + 0.0 + ], + [ + 0.0, 1.0 ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 + ] }, - "name": "Linear Function-23", - "type": { - "generic": "Linear" + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } - ], - "name": "slope", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 1.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(1,)", - "type": { - "PNL": "ParameterPort", - "generic": null } } - ], - "name": "word_input", - "output_ports": [ - { - "dtype": "float64", - "functions": [ - { + }, + "MappingProjection_from_OUTPUT_OutputPort_0__to_Conflict_Monitor_InputPort_0_": { + "parameters": { + "weight": 1 + }, + "sender": "OUTPUT", + "receiver": "Conflict_Monitor", + "sender_port": "OUTPUT_OutputPort_0", + "receiver_port": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_1": { + "function": "onnx::MatMul", "args": { - "PNL": { - "enable_output_type_conversion": false, - "execution_count": 0, - "has_initializers": false, - "output_type": 3, - "variable": [ - 0.0, + "B": [ + [ + 1.0, 0.0 + ], + [ + 0.0, + 1.0 ] - }, - "bounds": null, - "execute_until_finished": true, - "intercept": 0.0, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "slope": 1.0 + ] }, - "name": "Linear Function-26", - "type": { - "generic": "Linear" + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.5, + 0.5 + ], + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } - ], - "name": "OutputPort-0", - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "require_projection_in_composition": true, - "variable": [ - 0.0, - 0.0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "projections": null - }, - "shape": "(2,)", - "type": { - "PNL": "OutputPort", - "generic": null } } - ], - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "input_labels_dict": {}, - "input_port_variables": null, - "output_labels_dict": {}, - "previous_value": null, - "variable": [ - [ - 0.0, - 0.0 - ] - ] - }, - "execute_until_finished": true, - "input_ports": null, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "output_ports": null }, - "type": { - "PNL": "ProcessingMechanism", - "generic": null - } - } - }, - "parameters": { - "PNL": { - "execution_count": 0, - "has_initializers": false, - "node_ordering": [ - "color_input", - "color_hidden", - "OUTPUT", - "word_input", - "word_hidden", - "task_input", - "TASK", - "DECISION", - "CONTROL", - "Conflict Monitor" - ], - "required_node_roles": [], - "results": [], - "schedulers": { - "ContextFlags.PROCESSING": { - "conditions": { - "node": { - "DECISION": { - "args": [ - "OUTPUT", - 1 - ], - "function": null, - "kwargs": {}, - "type": "EveryNCalls" - }, - "OUTPUT": { - "args": [ - { - "args": [ - "color_hidden", - 1 - ], - "function": null, - "kwargs": {}, - "type": "EveryNCalls" - }, - { - "args": [ - "word_hidden", - 1 - ], - "function": null, - "kwargs": {}, - "type": "EveryNCalls" - } - ], - "function": null, - "kwargs": {}, - "type": "All" - }, - "color_hidden": { - "args": [ - "TASK", - 10 - ], - "function": null, - "kwargs": {}, - "type": "EveryNCalls" + "MappingProjection_from_Conflict_Monitor_OUTCOME__to_CONTROL_OUTCOME_": { + "parameters": { + "weight": 1 + }, + "sender": "Conflict_Monitor", + "receiver": "CONTROL", + "sender_port": "Conflict_Monitor_OUTCOME", + "receiver_port": "CONTROL_OUTCOME", + "metadata": { + "type": "MappingProjection", + "max_executions_before_finished": 1000, + "has_initializers": false, + "execute_until_finished": true, + "exponent": null, + "weight": null, + "functions": { + "LinearMatrix_Function_2": { + "function": "onnx::MatMul", + "args": { + "B": [ + [ + 1.0 + ] + ] }, - "word_hidden": { - "args": [ - "TASK", - 10 + "metadata": { + "type": "LinearMatrix", + "max_executions_before_finished": 1000, + "changes_shape": false, + "has_initializers": false, + "A": [ + 0.0 ], - "function": null, - "kwargs": {}, - "type": "EveryNCalls" - } - }, - "termination": { - "TimeScale.RUN": { - "args": [], - "function": null, - "kwargs": {}, - "type": "Never" - }, - "TimeScale.TRIAL": { - "args": [], - "function": null, - "kwargs": {}, - "type": "AllHaveRun" + "execute_until_finished": true, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "bounds": null, + "function_stateful_params": {} } } } } - }, - "simulation_results": [], - "variable": [ - 0 - ] - }, - "execute_until_finished": true, - "is_finished_flag": true, - "max_executions_before_finished": 1000, - "num_executions_before_finished": 0, - "retain_old_simulation_data": false - }, - "type": { - "PNL": "Composition", - "generic": "graph" + } + } } } - ] + } } From af6969bc8d540f005d45be24c55414c999cf2654 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Mon, 10 Jan 2022 21:52:19 -0500 Subject: [PATCH 248/285] JSON: support MDF v0.3.2, v0.3.3 --- .../_static/stroop_conflict_monitoring.json | 1704 ++++++++++------- psyneulink/core/components/component.py | 11 + .../core/components/functions/function.py | 2 +- .../nonstateful/distributionfunctions.py | 2 +- .../nonstateful/transferfunctions.py | 5 +- .../functions/userdefinedfunction.py | 1 - .../core/components/mechanisms/mechanism.py | 20 +- .../processing/integratormechanism.py | 4 + .../processing/transfermechanism.py | 23 +- psyneulink/core/compositions/composition.py | 2 +- psyneulink/core/globals/json.py | 32 +- requirements.txt | 2 +- 12 files changed, 1101 insertions(+), 707 deletions(-) diff --git a/docs/source/_static/stroop_conflict_monitoring.json b/docs/source/_static/stroop_conflict_monitoring.json index bc5ced098b6..e4a9e5c40cb 100644 --- a/docs/source/_static/stroop_conflict_monitoring.json +++ b/docs/source/_static/stroop_conflict_monitoring.json @@ -1,20 +1,19 @@ { "Stroop_model": { - "format": "ModECI MDF v0.3.1", - "generating_application": "PsyNeuLink v0.10.0.0+488.g0294251622", + "format": "ModECI MDF v0.3.3", + "generating_application": "PsyNeuLink v0.10.0.0+489.g58e55963a3", "graphs": { "Stroop_model": { - "parameters": {}, "conditions": { "node_specific": { - "color_hidden": { + "word_hidden": { "type": "EveryNCalls", "args": { "dependency": "TASK", "n": 10 } }, - "word_hidden": { + "color_hidden": { "type": "EveryNCalls", "args": { "dependency": "TASK", @@ -65,16 +64,16 @@ }, "metadata": { "type": "Composition", + "results": [], "input_specification": null, - "has_initializers": false, + "retain_old_simulation_data": false, + "simulation_results": [], "variable": [ 0 ], - "simulation_results": [], - "execute_until_finished": true, - "retain_old_simulation_data": false, "max_executions_before_finished": 1000, - "results": [], + "execute_until_finished": true, + "has_initializers": false, "node_ordering": [ "color_input", "color_hidden", @@ -97,58 +96,66 @@ "CONTROL": { "metadata": { "type": "ControlMechanism", - "output_labels_dict": {}, - "modulation": "multiplicative_param", "input_port_variables": null, - "has_initializers": false, - "outcome": null, + "output_labels_dict": {}, "variable": [ [ 1.0 ] ], "execute_until_finished": true, - "input_labels_dict": {}, + "has_initializers": false, + "outcome": null, + "modulation": "multiplicative_param", "simulation_ids": [], "max_executions_before_finished": 1000, - "control_signal_costs": null, + "input_labels_dict": {}, "net_outcome": null, - "reconfiguration_cost": null, + "control_signal_costs": null, + "input_ports": [ + "OUTCOME" + ], + "outcome_input_ports": "[(InputPort OUTCOME)]", + "costs": null, + "compute_net_outcome": "gANjZGlsbC5fZGlsbApfY3JlYXRlX2Z1bmN0aW9uCnEAKGNkaWxsLl9kaWxsCl9jcmVhdGVfY29k\nZQpxAShLAksASwJLAktDQwh8AHwBGABTAHECToVxAylYBwAAAG91dGNvbWVxBFgEAAAAY29zdHEF\nhnEGWGwAAAAvaG9tZS9rYXRoZXJpbmUvY29kZS9Qc3lOZXVMaW5rL3BzeW5ldWxpbmsvY29yZS9j\nb21wb25lbnRzL21lY2hhbmlzbXMvbW9kdWxhdG9yeS9jb250cm9sL2NvbnRyb2xtZWNoYW5pc20u\ncHlxB1gIAAAAPGxhbWJkYT5xCE2cBEMAcQkpKXRxClJxC2Nwc3luZXVsaW5rLmNvcmUuY29tcG9u\nZW50cy5tZWNoYW5pc21zLm1vZHVsYXRvcnkuY29udHJvbC5jb250cm9sbWVjaGFuaXNtCl9fZGlj\ndF9fCmgITk59cQxOdHENUnEOLg==\n", "objective_mechanism": { "Conflict_Monitor": { "metadata": { "type": "ObjectiveMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, + "has_initializers": false, + "output_ports": [ + "OUTCOME" + ], "input_ports": [ { "OUTPUT": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { @@ -162,33 +169,44 @@ }, "functions": { "OUTPUT_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "OUTPUT_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "OUTPUT_input_combination_function[0][0]", "args": { "variable0": "OUTPUT_input_combination_function" } }, "Logistic_Function_1": { - "function": "logistic", + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": 0.0, + "variable0": "OUTPUT_input_combination_function_dimreduce" + } + }, "args": { - "x_0": 0, - "bias": 0.0, - "gain": 1.0, "offset": 0.0, + "gain": 1.0, "scale": 1.0, + "x_0": 0, + "bias": 0.0, "variable0": "OUTPUT_input_combination_function_dimreduce" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -196,8 +214,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -216,13 +237,13 @@ "value": "Logistic_Function_1", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.5, 0.5 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -230,9 +251,6 @@ } } } - ], - "output_ports": [ - "OUTCOME" ] }, "input_ports": { @@ -241,10 +259,7 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": true, "variable": [ [ 0.0, @@ -252,9 +267,13 @@ ] ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": true, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": [ [ "OUTPUT.output_ports.OutputPort-0", @@ -265,52 +284,43 @@ } ] ], - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Stability_Function_0": { - "function": "energy", + "function": { + "energy": { + "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" + } + }, "args": { "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" }, "metadata": { "type": "Energy", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.NP_2D_ARRAY", "variable": [ [ 0.0, 0.0 ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": true, - "output_type": "FunctionOutputType.NP_2D_ARRAY", - "matrix": [ - [ - 0, - -2.5 - ], - [ - -2.5, - 0 - ] - ], - "metric": "energy", - "transfer_fct": null, "metric_fct": { "Distance_Function_4": { - "function": "distance", + "function": { + "distance": {} + }, "args": {}, "metadata": { "type": "Distance", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "variable": [ [ [ @@ -325,15 +335,29 @@ ] ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "metric": "energy", "normalize": false, "function_stateful_params": {} } } }, + "metric": "energy", + "matrix": [ + [ + 0, + -2.5 + ], + [ + -2.5, + 0 + ] + ], + "transfer_fct": null, "normalize": false, "function_stateful_params": {} } @@ -344,12 +368,12 @@ "value": "Stability_Function_0", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ -0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -357,10 +381,6 @@ } } }, - "outcome_input_ports_option": "separate", - "default_allocation": [ - 0.5 - ], "output_ports": [ { "name": "gain", @@ -368,9 +388,8 @@ "TASK": { "metadata": { "type": "LCAMechanism", - "output_labels_dict": {}, "input_port_variables": null, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, @@ -378,62 +397,19 @@ ] ], "execute_until_finished": true, - "input_labels_dict": {}, - "max_executions_before_finished": 1000, + "has_initializers": false, "termination_measure_value": 0.0, - "integrator_function_value": [ - [ - 0 - ] - ], - "output_ports": [ - "RESULTS" - ], - "termination_threshold": null, - "clip": null, - "has_recurrent_input_port": false, - "termination_measure": "max", + "max_executions_before_finished": 1000, + "input_labels_dict": {}, "input_ports": null, "matrix": "InverseHollowMatrix", - "termination_comparison_op": ">=", - "integrator_mode": true, - "combination_function": { - "LinearCombination_Function_23": { - "function": "linearcombination", - "args": { - "offset": 0.0, - "scale": 1.0 - }, - "metadata": { - "type": "LinearCombination", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": false, - "variable": [ - [ - 0.0, - 0.0 - ] - ], - "execute_until_finished": true, - "max_executions_before_finished": 1000, - "output_type": "FunctionOutputType.DEFAULT", - "weights": null, - "operation": "sum", - "exponents": null, - "function_stateful_params": {} - } - } - }, - "on_resume_integrator_mode": "current_value", - "enable_learning": false, "integrator_function": { "LeakyCompetingIntegrator_Function_0": { "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", "args": { - "rate": 0.5, "time_step_size": 0.1, "offset": 0.0, + "rate": 0.5, "noise": 0.0 }, "metadata": { @@ -444,9 +420,6 @@ 0.5 ] ], - "changes_shape": false, - "has_initializers": true, - "enable_output_type_conversion": false, "variable": [ [ 0.0, @@ -454,8 +427,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": true, + "enable_output_type_conversion": false, "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, "function_stateful_params": { "previous_value": { "id": "previous_value", @@ -470,7 +446,56 @@ } } } - } + }, + "combination_function": { + "LinearCombination_Function_23": { + "function": { + "linearcombination": { + "offset": 0.0, + "scale": 1.0 + } + }, + "args": { + "offset": 0.0, + "scale": 1.0 + }, + "metadata": { + "type": "LinearCombination", + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "has_initializers": false, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, + "exponents": null, + "weights": null, + "operation": "sum", + "function_stateful_params": {} + } + } + }, + "integrator_mode": true, + "has_recurrent_input_port": false, + "on_resume_integrator_mode": "current_value", + "clip": null, + "output_ports": [ + "RESULTS" + ], + "enable_learning": false, + "integrator_function_value": [ + [ + 0 + ] + ], + "termination_comparison_op": ">=", + "termination_threshold": null, + "termination_measure": "max" }, "input_ports": { "TASK_input_port_TASK_recurrent_projection": { @@ -484,33 +509,44 @@ }, "functions": { "TASK_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "TASK_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "TASK_input_combination_function[0][0]", "args": { "variable0": "TASK_input_combination_function" } }, "Logistic_Function_5": { - "function": "logistic", + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": 0.0, + "variable0": "LeakyCompetingIntegrator_Function_0" + } + }, "args": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, "x_0": 0, "bias": 0.0, - "gain": 1.0, - "offset": 0.0, - "scale": 1.0, "variable0": "LeakyCompetingIntegrator_Function_0" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -518,8 +554,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -528,11 +567,11 @@ } }, "LeakyCompetingIntegrator_Function_0": { - "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", + "value": "previous_value + (-0.5 * previous_value + TASK_input_combination_function_dimreduce) * 0.1 + 0.0 * (0.1 ** 0.5)", "args": { - "rate": 0.5, "time_step_size": 0.1, "offset": 0.0, + "rate": 0.5, "noise": 0.0, "variable0": "TASK_input_combination_function_dimreduce" }, @@ -544,9 +583,6 @@ 0.5 ] ], - "changes_shape": false, - "has_initializers": true, - "enable_output_type_conversion": false, "variable": [ [ 0.0, @@ -554,8 +590,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": true, + "enable_output_type_conversion": false, "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, "function_stateful_params": { "previous_value": { "id": "previous_value", @@ -572,18 +611,18 @@ } }, "parameters": { + "hetero": { + "value": -1.0 + }, + "smoothing_factor": { + "value": 0.5 + }, "auto": { "value": 0.0 }, "competition": { "value": 1.0 }, - "smoothing_factor": { - "value": 0.5 - }, - "hetero": { - "value": -1.0 - }, "combination_function_input_data": { "value": "[TASK_input_port_TASK_recurrent_projection, TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_]" }, @@ -602,13 +641,13 @@ "value": "Logistic_Function_5", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.5, 0.5 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -618,15 +657,14 @@ } } ], - "costs": null, - "compute_reconfiguration_cost": null, - "combine_costs": "gANjbnVtcHkKc3VtCnEALg==\n", - "input_ports": [ - "OUTCOME" + "default_allocation": [ + 0.5 ], - "outcome_input_ports": "[(InputPort OUTCOME)]", - "monitor_for_control": [], - "compute_net_outcome": "gANjZGlsbC5fZGlsbApfY3JlYXRlX2Z1bmN0aW9uCnEAKGNkaWxsLl9kaWxsCl9jcmVhdGVfY29k\nZQpxAShLAksASwJLAktDQwh8AHwBGABTAHECToVxAylYBwAAAG91dGNvbWVxBFgEAAAAY29zdHEF\nhnEGWGwAAAAvaG9tZS9rYXRoZXJpbmUvY29kZS9Qc3lOZXVMaW5rL3BzeW5ldWxpbmsvY29yZS9j\nb21wb25lbnRzL21lY2hhbmlzbXMvbW9kdWxhdG9yeS9jb250cm9sL2NvbnRyb2xtZWNoYW5pc20u\ncHlxB1gIAAAAPGxhbWJkYT5xCE2cBEMAcQkpKXRxClJxC2Nwc3luZXVsaW5rLmNvcmUuY29tcG9u\nZW50cy5tZWNoYW5pc21zLm1vZHVsYXRvcnkuY29udHJvbC5jb250cm9sbWVjaGFuaXNtCl9fZGlj\ndF9fCmgITk59cQxOdHENUnEOLg==\n" + "reconfiguration_cost": null, + "outcome_input_ports_option": "separate", + "combine_costs": "gANjbnVtcHkKc3VtCnEALg==\n", + "compute_reconfiguration_cost": null, + "monitor_for_control": [] }, "input_ports": { "CONTROL_OUTCOME": { @@ -634,43 +672,48 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": true, "variable": [ 0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": true, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": null, - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Default_Control_Function_0": { - "function": "defaultallocationfunction", + "function": { + "defaultallocationfunction": { + "num_control_signals": 1, + "variable0": "CONTROL_OUTCOME" + } + }, "args": { "num_control_signals": 1, "variable0": "CONTROL_OUTCOME" }, "metadata": { "type": "DefaultAllocationFunction", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.NP_2D_ARRAY", "variable": [ [ 1.0 ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": true, - "output_type": "FunctionOutputType.NP_2D_ARRAY", "function_stateful_params": {} } } @@ -681,14 +724,12 @@ "metadata": { "type": "ControlSignal", "require_projection_in_composition": true, - "has_initializers": false, "variable": [ 0.5 ], "execute_until_finished": true, + "has_initializers": false, "max_executions_before_finished": 1000, - "modulation": "multiplicative_param", - "allocation_samples": null, "projections": [ [ "TASK.input_ports.gain", @@ -698,7 +739,9 @@ "PROJECTION_TYPE": "ControlProjection" } ] - ] + ], + "modulation": "multiplicative_param", + "allocation_samples": null } } } @@ -709,20 +752,20 @@ "color_input": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "color_input_InputPort_0": { @@ -730,46 +773,52 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": false, "variable": [ 0.0, 0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": false, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": null, - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Linear_Function_3": { - "function": "linear", + "function": { + "linear": { + "slope": 1.0, + "intercept": 0.0, + "variable0": "color_input_InputPort_0" + } + }, "args": { - "intercept": 0.0, "slope": 1.0, + "intercept": 0.0, "variable0": "color_input_InputPort_0" }, "metadata": { "type": "Linear", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.NP_2D_ARRAY", "variable": [ [ 0.0, 0.0 ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": true, - "output_type": "FunctionOutputType.NP_2D_ARRAY", "bounds": null, "function_stateful_params": {} } @@ -780,13 +829,13 @@ "value": "Linear_Function_3", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.0, 0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -796,20 +845,20 @@ "color_hidden": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "color_hidden_input_port_MappingProjection_from_color_input_OutputPort_0__to_color_hidden_InputPort_0_": { @@ -823,33 +872,44 @@ }, "functions": { "color_hidden_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "color_hidden_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "color_hidden_input_combination_function[0][0]", "args": { "variable0": "color_hidden_input_combination_function" } }, "Logistic_Function_0": { - "function": "logistic", + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": -4.0, + "variable0": "color_hidden_input_combination_function_dimreduce" + } + }, "args": { - "x_0": 0, - "bias": -4.0, - "gain": 1.0, "offset": 0.0, + "gain": 1.0, "scale": 1.0, + "x_0": 0, + "bias": -4.0, "variable0": "color_hidden_input_combination_function_dimreduce" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -857,8 +917,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -877,13 +940,13 @@ "value": "Logistic_Function_0", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.017986209962091562, 0.017986209962091562 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -893,20 +956,20 @@ "OUTPUT": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { @@ -920,33 +983,44 @@ }, "functions": { "OUTPUT_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "OUTPUT_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "OUTPUT_input_combination_function[0][0]", "args": { "variable0": "OUTPUT_input_combination_function" } }, "Logistic_Function_1": { - "function": "logistic", + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": 0.0, + "variable0": "OUTPUT_input_combination_function_dimreduce" + } + }, "args": { - "x_0": 0, - "bias": 0.0, - "gain": 1.0, "offset": 0.0, + "gain": 1.0, "scale": 1.0, + "x_0": 0, + "bias": 0.0, "variable0": "OUTPUT_input_combination_function_dimreduce" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -954,8 +1028,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -974,13 +1051,13 @@ "value": "Logistic_Function_1", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.5, 0.5 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -990,20 +1067,20 @@ "word_input": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "word_input_InputPort_0": { @@ -1011,46 +1088,52 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": false, "variable": [ 0.0, 0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": false, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": null, - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Linear_Function_22": { - "function": "linear", + "function": { + "linear": { + "slope": 1.0, + "intercept": 0.0, + "variable0": "word_input_InputPort_0" + } + }, "args": { - "intercept": 0.0, "slope": 1.0, + "intercept": 0.0, "variable0": "word_input_InputPort_0" }, "metadata": { "type": "Linear", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.NP_2D_ARRAY", "variable": [ [ 0.0, 0.0 ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": true, - "output_type": "FunctionOutputType.NP_2D_ARRAY", "bounds": null, "function_stateful_params": {} } @@ -1061,13 +1144,13 @@ "value": "Linear_Function_22", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.0, 0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -1077,20 +1160,20 @@ "word_hidden": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "word_hidden_input_port_MappingProjection_from_word_input_OutputPort_0__to_word_hidden_InputPort_0_": { @@ -1104,33 +1187,44 @@ }, "functions": { "word_hidden_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "word_hidden_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "word_hidden_input_combination_function[0][0]", "args": { "variable0": "word_hidden_input_combination_function" } }, "Logistic_Function_2": { - "function": "logistic", + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": -4.0, + "variable0": "word_hidden_input_combination_function_dimreduce" + } + }, "args": { - "x_0": 0, - "bias": -4.0, - "gain": 1.0, "offset": 0.0, + "gain": 1.0, "scale": 1.0, + "x_0": 0, + "bias": -4.0, "variable0": "word_hidden_input_combination_function_dimreduce" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -1138,8 +1232,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -1158,13 +1255,13 @@ "value": "Logistic_Function_2", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.017986209962091562, 0.017986209962091562 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -1174,20 +1271,20 @@ "task_input": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "task_input_InputPort_0": { @@ -1195,46 +1292,52 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": false, "variable": [ 0.0, 0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": false, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": null, - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Linear_Function_34": { - "function": "linear", + "function": { + "linear": { + "slope": 1.0, + "intercept": 0.0, + "variable0": "task_input_InputPort_0" + } + }, "args": { - "intercept": 0.0, "slope": 1.0, + "intercept": 0.0, "variable0": "task_input_InputPort_0" }, "metadata": { "type": "Linear", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.NP_2D_ARRAY", "variable": [ [ 0.0, 0.0 ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": true, - "output_type": "FunctionOutputType.NP_2D_ARRAY", "bounds": null, "function_stateful_params": {} } @@ -1245,13 +1348,13 @@ "value": "Linear_Function_34", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.0, 0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -1261,9 +1364,8 @@ "TASK": { "metadata": { "type": "LCAMechanism", - "output_labels_dict": {}, "input_port_variables": null, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, @@ -1271,62 +1373,19 @@ ] ], "execute_until_finished": true, - "input_labels_dict": {}, - "max_executions_before_finished": 1000, + "has_initializers": false, "termination_measure_value": 0.0, - "integrator_function_value": [ - [ - 0 - ] - ], - "output_ports": [ - "RESULTS" - ], - "termination_threshold": null, - "clip": null, - "has_recurrent_input_port": false, - "termination_measure": "max", + "max_executions_before_finished": 1000, + "input_labels_dict": {}, "input_ports": null, "matrix": "InverseHollowMatrix", - "termination_comparison_op": ">=", - "integrator_mode": true, - "combination_function": { - "LinearCombination_Function_23": { - "function": "linearcombination", - "args": { - "offset": 0.0, - "scale": 1.0 - }, - "metadata": { - "type": "LinearCombination", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": false, - "variable": [ - [ - 0.0, - 0.0 - ] - ], - "execute_until_finished": true, - "max_executions_before_finished": 1000, - "output_type": "FunctionOutputType.DEFAULT", - "weights": null, - "operation": "sum", - "exponents": null, - "function_stateful_params": {} - } - } - }, - "on_resume_integrator_mode": "current_value", - "enable_learning": false, "integrator_function": { "LeakyCompetingIntegrator_Function_0": { "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", "args": { - "rate": 0.5, "time_step_size": 0.1, "offset": 0.0, + "rate": 0.5, "noise": 0.0 }, "metadata": { @@ -1337,9 +1396,6 @@ 0.5 ] ], - "changes_shape": false, - "has_initializers": true, - "enable_output_type_conversion": false, "variable": [ [ 0.0, @@ -1347,8 +1403,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": true, + "enable_output_type_conversion": false, "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, "function_stateful_params": { "previous_value": { "id": "previous_value", @@ -1363,7 +1422,56 @@ } } } - } + }, + "combination_function": { + "LinearCombination_Function_23": { + "function": { + "linearcombination": { + "offset": 0.0, + "scale": 1.0 + } + }, + "args": { + "offset": 0.0, + "scale": 1.0 + }, + "metadata": { + "type": "LinearCombination", + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "has_initializers": false, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, + "exponents": null, + "weights": null, + "operation": "sum", + "function_stateful_params": {} + } + } + }, + "integrator_mode": true, + "has_recurrent_input_port": false, + "on_resume_integrator_mode": "current_value", + "clip": null, + "output_ports": [ + "RESULTS" + ], + "enable_learning": false, + "integrator_function_value": [ + [ + 0 + ] + ], + "termination_comparison_op": ">=", + "termination_threshold": null, + "termination_measure": "max" }, "input_ports": { "TASK_input_port_TASK_recurrent_projection": { @@ -1377,33 +1485,44 @@ }, "functions": { "TASK_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "TASK_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "TASK_input_combination_function[0][0]", "args": { "variable0": "TASK_input_combination_function" } }, "Logistic_Function_5": { - "function": "logistic", + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": 0.0, + "variable0": "LeakyCompetingIntegrator_Function_0" + } + }, "args": { - "x_0": 0, - "bias": 0.0, - "gain": 1.0, "offset": 0.0, + "gain": 1.0, "scale": 1.0, + "x_0": 0, + "bias": 0.0, "variable0": "LeakyCompetingIntegrator_Function_0" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -1411,8 +1530,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -1421,11 +1543,11 @@ } }, "LeakyCompetingIntegrator_Function_0": { - "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", + "value": "previous_value + (-0.5 * previous_value + TASK_input_combination_function_dimreduce) * 0.1 + 0.0 * (0.1 ** 0.5)", "args": { - "rate": 0.5, "time_step_size": 0.1, "offset": 0.0, + "rate": 0.5, "noise": 0.0, "variable0": "TASK_input_combination_function_dimreduce" }, @@ -1437,9 +1559,6 @@ 0.5 ] ], - "changes_shape": false, - "has_initializers": true, - "enable_output_type_conversion": false, "variable": [ [ 0.0, @@ -1447,8 +1566,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": true, + "enable_output_type_conversion": false, "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, "function_stateful_params": { "previous_value": { "id": "previous_value", @@ -1465,18 +1587,18 @@ } }, "parameters": { + "hetero": { + "value": -1.0 + }, + "smoothing_factor": { + "value": 0.5 + }, "auto": { "value": 0.0 }, "competition": { "value": 1.0 }, - "smoothing_factor": { - "value": 0.5 - }, - "hetero": { - "value": -1.0 - }, "combination_function_input_data": { "value": "[TASK_input_port_TASK_recurrent_projection, TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_]" }, @@ -1495,13 +1617,13 @@ "value": "Logistic_Function_5", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.5, 0.5 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -1511,21 +1633,17 @@ "DECISION": { "metadata": { "type": "DDM", - "output_labels_dict": {}, "input_port_variables": null, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0 ] ], "execute_until_finished": true, - "input_labels_dict": {}, + "has_initializers": false, "max_executions_before_finished": 1000, - "output_ports": [ - "DECISION_VARIABLE", - "RESPONSE_TIME" - ], + "input_labels_dict": {}, "input_ports": [ { "name": "ARRAY", @@ -1537,36 +1655,45 @@ ], "function": { "Reduce_Function_0_2": { - "function": "reduce", + "function": { + "reduce": { + "offset": 0.0, + "scale": 1.0 + } + }, "args": { "offset": 0.0, "scale": 1.0 }, "metadata": { "type": "Reduce", - "changes_shape": true, - "has_initializers": false, - "enable_output_type_conversion": false, "variable": [ 0 ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": false, "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": true, + "max_executions_before_finished": 1000, + "exponents": null, "weights": [ 1, -1 ], "operation": "sum", - "exponents": null, "function_stateful_params": {} } } } } ], + "random_state": null, "input_format": "SCALAR", - "random_state": null + "output_ports": [ + "DECISION_VARIABLE", + "RESPONSE_TIME" + ] }, "input_ports": { "DECISION_ARRAY": { @@ -1574,10 +1701,7 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": false, "variable": [ [ 0.0, @@ -1585,24 +1709,41 @@ ] ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": false, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": null, - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Drift_Diffusion_Analytical_Function_0_3": { - "function": "driftdiffusionanalytical", + "function": { + "driftdiffusionanalytical": { + "drift_rate": 1.0, + "threshold": 1.0, + "non_decision_time": 0.2, + "starting_value": 0.0, + "bias": 0.5, + "noise": 0.5, + "shape": [ + 1, + 1 + ], + "variable0": "DECISION_ARRAY" + } + }, "args": { - "bias": 0.5, - "starting_value": 0.0, - "threshold": 1.0, "drift_rate": 1.0, + "threshold": 1.0, "non_decision_time": 0.2, + "starting_value": 0.0, + "bias": 0.5, "noise": 0.5, "shape": [ 1, @@ -1612,17 +1753,17 @@ }, "metadata": { "type": "DriftDiffusionAnalytical", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0 ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "function_stateful_params": {} } } @@ -1644,12 +1785,12 @@ "value": "Drift_Diffusion_Analytical_Function_0_3[0]", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ - -1.0 + 1.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -1658,12 +1799,12 @@ "value": "Drift_Diffusion_Analytical_Function_0_3[1]", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 4.2 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -1673,37 +1814,40 @@ "Conflict_Monitor": { "metadata": { "type": "ObjectiveMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, + "has_initializers": false, + "output_ports": [ + "OUTCOME" + ], "input_ports": [ { "OUTPUT": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { @@ -1717,33 +1861,44 @@ }, "functions": { "OUTPUT_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "OUTPUT_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "OUTPUT_input_combination_function[0][0]", "args": { "variable0": "OUTPUT_input_combination_function" } }, "Logistic_Function_1": { - "function": "logistic", - "args": { - "x_0": 0, - "bias": 0.0, - "gain": 1.0, - "offset": 0.0, - "scale": 1.0, + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": 0.0, + "variable0": "OUTPUT_input_combination_function_dimreduce" + } + }, + "args": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": 0.0, "variable0": "OUTPUT_input_combination_function_dimreduce" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -1751,8 +1906,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -1771,13 +1929,13 @@ "value": "Logistic_Function_1", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.5, 0.5 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -1785,9 +1943,6 @@ } } } - ], - "output_ports": [ - "OUTCOME" ] }, "input_ports": { @@ -1796,10 +1951,7 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": true, "variable": [ [ 0.0, @@ -1807,9 +1959,13 @@ ] ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": true, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": [ [ "OUTPUT.output_ports.OutputPort-0", @@ -1820,52 +1976,43 @@ } ] ], - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Stability_Function_0": { - "function": "energy", + "function": { + "energy": { + "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" + } + }, "args": { "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" }, "metadata": { "type": "Energy", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.NP_2D_ARRAY", "variable": [ [ 0.0, 0.0 ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": true, - "output_type": "FunctionOutputType.NP_2D_ARRAY", - "matrix": [ - [ - 0, - -2.5 - ], - [ - -2.5, - 0 - ] - ], - "metric": "energy", - "transfer_fct": null, "metric_fct": { "Distance_Function_4": { - "function": "distance", + "function": { + "distance": {} + }, "args": {}, "metadata": { "type": "Distance", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "variable": [ [ [ @@ -1880,15 +2027,29 @@ ] ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "metric": "energy", "normalize": false, "function_stateful_params": {} } } }, + "metric": "energy", + "matrix": [ + [ + 0, + -2.5 + ], + [ + -2.5, + 0 + ] + ], + "transfer_fct": null, "normalize": false, "function_stateful_params": {} } @@ -1899,12 +2060,12 @@ "value": "Stability_Function_0", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ -0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -1914,58 +2075,66 @@ "CONTROL": { "metadata": { "type": "ControlMechanism", - "output_labels_dict": {}, - "modulation": "multiplicative_param", "input_port_variables": null, - "has_initializers": false, - "outcome": null, + "output_labels_dict": {}, "variable": [ [ 1.0 ] ], "execute_until_finished": true, - "input_labels_dict": {}, + "has_initializers": false, + "outcome": null, + "modulation": "multiplicative_param", "simulation_ids": [], "max_executions_before_finished": 1000, - "control_signal_costs": null, + "input_labels_dict": {}, "net_outcome": null, - "reconfiguration_cost": null, + "control_signal_costs": null, + "input_ports": [ + "OUTCOME" + ], + "outcome_input_ports": "[(InputPort OUTCOME)]", + "costs": null, + "compute_net_outcome": "gANjZGlsbC5fZGlsbApfY3JlYXRlX2Z1bmN0aW9uCnEAKGNkaWxsLl9kaWxsCl9jcmVhdGVfY29k\nZQpxAShLAksASwJLAktDQwh8AHwBGABTAHECToVxAylYBwAAAG91dGNvbWVxBFgEAAAAY29zdHEF\nhnEGWGwAAAAvaG9tZS9rYXRoZXJpbmUvY29kZS9Qc3lOZXVMaW5rL3BzeW5ldWxpbmsvY29yZS9j\nb21wb25lbnRzL21lY2hhbmlzbXMvbW9kdWxhdG9yeS9jb250cm9sL2NvbnRyb2xtZWNoYW5pc20u\ncHlxB1gIAAAAPGxhbWJkYT5xCE2cBEMAcQkpKXRxClJxC2Nwc3luZXVsaW5rLmNvcmUuY29tcG9u\nZW50cy5tZWNoYW5pc21zLm1vZHVsYXRvcnkuY29udHJvbC5jb250cm9sbWVjaGFuaXNtCl9fZGlj\ndF9fCmgITk59cQxOdHENUnEOLg==\n", "objective_mechanism": { "Conflict_Monitor": { "metadata": { "type": "ObjectiveMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, + "has_initializers": false, + "output_ports": [ + "OUTCOME" + ], "input_ports": [ { "OUTPUT": { "metadata": { "type": "ProcessingMechanism", - "input_port_variables": null, - "max_executions_before_finished": 1000, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, 0.0 ] ], + "input_port_variables": null, "execute_until_finished": true, - "output_labels_dict": {}, + "max_executions_before_finished": 1000, "input_labels_dict": {}, - "input_ports": null, - "output_ports": null + "has_initializers": false, + "output_ports": null, + "input_ports": null }, "input_ports": { "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_": { @@ -1979,33 +2148,44 @@ }, "functions": { "OUTPUT_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "OUTPUT_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "OUTPUT_input_combination_function[0][0]", "args": { "variable0": "OUTPUT_input_combination_function" } }, "Logistic_Function_1": { - "function": "logistic", + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": 0.0, + "variable0": "OUTPUT_input_combination_function_dimreduce" + } + }, "args": { - "x_0": 0, - "bias": 0.0, - "gain": 1.0, "offset": 0.0, + "gain": 1.0, "scale": 1.0, + "x_0": 0, + "bias": 0.0, "variable0": "OUTPUT_input_combination_function_dimreduce" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -2013,8 +2193,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -2033,13 +2216,13 @@ "value": "Logistic_Function_1", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.5, 0.5 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -2047,9 +2230,6 @@ } } } - ], - "output_ports": [ - "OUTCOME" ] }, "input_ports": { @@ -2058,10 +2238,7 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": true, "variable": [ [ 0.0, @@ -2069,9 +2246,13 @@ ] ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": true, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": [ [ "OUTPUT.output_ports.OutputPort-0", @@ -2082,52 +2263,43 @@ } ] ], - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Stability_Function_0": { - "function": "energy", + "function": { + "energy": { + "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" + } + }, "args": { "variable0": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_" }, "metadata": { "type": "Energy", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.NP_2D_ARRAY", "variable": [ [ 0.0, 0.0 ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": true, - "output_type": "FunctionOutputType.NP_2D_ARRAY", - "matrix": [ - [ - 0, - -2.5 - ], - [ - -2.5, - 0 - ] - ], - "metric": "energy", - "transfer_fct": null, "metric_fct": { "Distance_Function_4": { - "function": "distance", + "function": { + "distance": {} + }, "args": {}, "metadata": { "type": "Distance", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "variable": [ [ [ @@ -2142,15 +2314,29 @@ ] ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "metric": "energy", "normalize": false, "function_stateful_params": {} } } }, + "metric": "energy", + "matrix": [ + [ + 0, + -2.5 + ], + [ + -2.5, + 0 + ] + ], + "transfer_fct": null, "normalize": false, "function_stateful_params": {} } @@ -2161,12 +2347,12 @@ "value": "Stability_Function_0", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ -0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -2174,10 +2360,6 @@ } } }, - "outcome_input_ports_option": "separate", - "default_allocation": [ - 0.5 - ], "output_ports": [ { "name": "gain", @@ -2185,9 +2367,8 @@ "TASK": { "metadata": { "type": "LCAMechanism", - "output_labels_dict": {}, "input_port_variables": null, - "has_initializers": false, + "output_labels_dict": {}, "variable": [ [ 0.0, @@ -2195,62 +2376,19 @@ ] ], "execute_until_finished": true, - "input_labels_dict": {}, - "max_executions_before_finished": 1000, + "has_initializers": false, "termination_measure_value": 0.0, - "integrator_function_value": [ - [ - 0 - ] - ], - "output_ports": [ - "RESULTS" - ], - "termination_threshold": null, - "clip": null, - "has_recurrent_input_port": false, - "termination_measure": "max", + "max_executions_before_finished": 1000, + "input_labels_dict": {}, "input_ports": null, "matrix": "InverseHollowMatrix", - "termination_comparison_op": ">=", - "integrator_mode": true, - "combination_function": { - "LinearCombination_Function_23": { - "function": "linearcombination", - "args": { - "offset": 0.0, - "scale": 1.0 - }, - "metadata": { - "type": "LinearCombination", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": false, - "variable": [ - [ - 0.0, - 0.0 - ] - ], - "execute_until_finished": true, - "max_executions_before_finished": 1000, - "output_type": "FunctionOutputType.DEFAULT", - "weights": null, - "operation": "sum", - "exponents": null, - "function_stateful_params": {} - } - } - }, - "on_resume_integrator_mode": "current_value", - "enable_learning": false, "integrator_function": { "LeakyCompetingIntegrator_Function_0": { "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", "args": { - "rate": 0.5, "time_step_size": 0.1, "offset": 0.0, + "rate": 0.5, "noise": 0.0 }, "metadata": { @@ -2261,9 +2399,6 @@ 0.5 ] ], - "changes_shape": false, - "has_initializers": true, - "enable_output_type_conversion": false, "variable": [ [ 0.0, @@ -2271,8 +2406,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": true, + "enable_output_type_conversion": false, "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, "function_stateful_params": { "previous_value": { "id": "previous_value", @@ -2287,7 +2425,56 @@ } } } - } + }, + "combination_function": { + "LinearCombination_Function_23": { + "function": { + "linearcombination": { + "offset": 0.0, + "scale": 1.0 + } + }, + "args": { + "offset": 0.0, + "scale": 1.0 + }, + "metadata": { + "type": "LinearCombination", + "variable": [ + [ + 0.0, + 0.0 + ] + ], + "execute_until_finished": true, + "has_initializers": false, + "enable_output_type_conversion": false, + "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, + "exponents": null, + "weights": null, + "operation": "sum", + "function_stateful_params": {} + } + } + }, + "integrator_mode": true, + "has_recurrent_input_port": false, + "on_resume_integrator_mode": "current_value", + "clip": null, + "output_ports": [ + "RESULTS" + ], + "enable_learning": false, + "integrator_function_value": [ + [ + 0 + ] + ], + "termination_comparison_op": ">=", + "termination_threshold": null, + "termination_measure": "max" }, "input_ports": { "TASK_input_port_TASK_recurrent_projection": { @@ -2301,33 +2488,44 @@ }, "functions": { "TASK_input_combination_function": { - "function": "onnx::ReduceSum", + "function": { + "onnx::ReduceSum": { + "data": "combination_function_input_data", + "axes": 0 + } + }, "args": { "data": "combination_function_input_data", "axes": 0 } }, "TASK_input_combination_function_dimreduce": { - "value": "variable0[0][0]", + "value": "TASK_input_combination_function[0][0]", "args": { "variable0": "TASK_input_combination_function" } }, "Logistic_Function_5": { - "function": "logistic", + "function": { + "logistic": { + "offset": 0.0, + "gain": 1.0, + "scale": 1.0, + "x_0": 0, + "bias": 0.0, + "variable0": "LeakyCompetingIntegrator_Function_0" + } + }, "args": { - "x_0": 0, - "bias": 0.0, - "gain": 1.0, "offset": 0.0, + "gain": 1.0, "scale": 1.0, + "x_0": 0, + "bias": 0.0, "variable0": "LeakyCompetingIntegrator_Function_0" }, "metadata": { "type": "Logistic", - "changes_shape": false, - "has_initializers": false, - "enable_output_type_conversion": true, "variable": [ [ 0.0, @@ -2335,8 +2533,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": false, + "enable_output_type_conversion": true, "output_type": "FunctionOutputType.NP_2D_ARRAY", + "changes_shape": false, + "max_executions_before_finished": 1000, "bounds": [ 0, 1 @@ -2345,11 +2546,11 @@ } }, "LeakyCompetingIntegrator_Function_0": { - "value": "previous_value + (-rate * previous_value + variable0) * time_step_size + noise * (time_step_size ** 0.5)", + "value": "previous_value + (-0.5 * previous_value + TASK_input_combination_function_dimreduce) * 0.1 + 0.0 * (0.1 ** 0.5)", "args": { - "rate": 0.5, "time_step_size": 0.1, "offset": 0.0, + "rate": 0.5, "noise": 0.0, "variable0": "TASK_input_combination_function_dimreduce" }, @@ -2361,9 +2562,6 @@ 0.5 ] ], - "changes_shape": false, - "has_initializers": true, - "enable_output_type_conversion": false, "variable": [ [ 0.0, @@ -2371,8 +2569,11 @@ ] ], "execute_until_finished": true, - "max_executions_before_finished": 1000, + "has_initializers": true, + "enable_output_type_conversion": false, "output_type": "FunctionOutputType.DEFAULT", + "changes_shape": false, + "max_executions_before_finished": 1000, "function_stateful_params": { "previous_value": { "id": "previous_value", @@ -2389,18 +2590,18 @@ } }, "parameters": { + "hetero": { + "value": -1.0 + }, + "smoothing_factor": { + "value": 0.5 + }, "auto": { "value": 0.0 }, "competition": { "value": 1.0 }, - "smoothing_factor": { - "value": 0.5 - }, - "hetero": { - "value": -1.0 - }, "combination_function_input_data": { "value": "[TASK_input_port_TASK_recurrent_projection, TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_]" }, @@ -2419,13 +2620,13 @@ "value": "Logistic_Function_5", "metadata": { "type": "OutputPort", - "max_executions_before_finished": 1000, - "has_initializers": false, "variable": [ 0.5, 0.5 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "require_projection_in_composition": true, "projections": null } @@ -2435,15 +2636,14 @@ } } ], - "costs": null, - "compute_reconfiguration_cost": null, - "combine_costs": "gANjbnVtcHkKc3VtCnEALg==\n", - "input_ports": [ - "OUTCOME" + "default_allocation": [ + 0.5 ], - "outcome_input_ports": "[(InputPort OUTCOME)]", - "monitor_for_control": [], - "compute_net_outcome": "gANjZGlsbC5fZGlsbApfY3JlYXRlX2Z1bmN0aW9uCnEAKGNkaWxsLl9kaWxsCl9jcmVhdGVfY29k\nZQpxAShLAksASwJLAktDQwh8AHwBGABTAHECToVxAylYBwAAAG91dGNvbWVxBFgEAAAAY29zdHEF\nhnEGWGwAAAAvaG9tZS9rYXRoZXJpbmUvY29kZS9Qc3lOZXVMaW5rL3BzeW5ldWxpbmsvY29yZS9j\nb21wb25lbnRzL21lY2hhbmlzbXMvbW9kdWxhdG9yeS9jb250cm9sL2NvbnRyb2xtZWNoYW5pc20u\ncHlxB1gIAAAAPGxhbWJkYT5xCE2cBEMAcQkpKXRxClJxC2Nwc3luZXVsaW5rLmNvcmUuY29tcG9u\nZW50cy5tZWNoYW5pc21zLm1vZHVsYXRvcnkuY29udHJvbC5jb250cm9sbWVjaGFuaXNtCl9fZGlj\ndF9fCmgITk59cQxOdHENUnEOLg==\n" + "reconfiguration_cost": null, + "outcome_input_ports_option": "separate", + "combine_costs": "gANjbnVtcHkKc3VtCnEALg==\n", + "compute_reconfiguration_cost": null, + "monitor_for_control": [] }, "input_ports": { "CONTROL_OUTCOME": { @@ -2451,43 +2651,48 @@ "type": "float64", "metadata": { "type": "InputPort", - "max_executions_before_finished": 1000, "shadow_inputs": null, - "has_initializers": false, - "internal_only": true, "variable": [ 0.0 ], "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, + "internal_only": true, "require_projection_in_composition": true, - "combine": null, "exponent": null, + "default_input": null, + "combine": null, "projections": null, - "weight": null, - "default_input": null + "weight": null } } }, "functions": { "Default_Control_Function_0": { - "function": "defaultallocationfunction", + "function": { + "defaultallocationfunction": { + "num_control_signals": 1, + "variable0": "CONTROL_OUTCOME" + } + }, "args": { "num_control_signals": 1, "variable0": "CONTROL_OUTCOME" }, "metadata": { "type": "DefaultAllocationFunction", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.NP_2D_ARRAY", "variable": [ [ 1.0 ] ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": true, - "output_type": "FunctionOutputType.NP_2D_ARRAY", "function_stateful_params": {} } } @@ -2498,14 +2703,12 @@ "metadata": { "type": "ControlSignal", "require_projection_in_composition": true, - "has_initializers": false, "variable": [ 0.5 ], "execute_until_finished": true, + "has_initializers": false, "max_executions_before_finished": 1000, - "modulation": "multiplicative_param", - "allocation_samples": null, "projections": [ [ "TASK.input_ports.gain", @@ -2515,7 +2718,9 @@ "PROJECTION_TYPE": "ControlProjection" } ] - ] + ], + "modulation": "multiplicative_param", + "allocation_samples": null } } } @@ -2532,14 +2737,27 @@ "receiver_port": "color_hidden_input_port_MappingProjection_from_color_input_OutputPort_0__to_color_hidden_InputPort_0_", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_3": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 2.0, + -2.0 + ], + [ + -2.0, + 2.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2554,16 +2772,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.0, 0.0 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2581,14 +2799,27 @@ "receiver_port": "OUTPUT_input_port_MappingProjection_from_color_hidden_OutputPort_0__to_OUTPUT_InputPort_0_", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_4": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 2.0, + -2.0 + ], + [ + -2.0, + 2.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2603,16 +2834,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.017986209962091562, 0.017986209962091562 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2630,14 +2861,27 @@ "receiver_port": "word_hidden_input_port_MappingProjection_from_word_input_OutputPort_0__to_word_hidden_InputPort_0_", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_7": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 3.0, + -3.0 + ], + [ + -3.0, + 3.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2652,16 +2896,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.0, 0.0 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2679,14 +2923,27 @@ "receiver_port": "OUTPUT_input_port_MappingProjection_from_word_hidden_OutputPort_0__to_OUTPUT_InputPort_0_", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_8": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 3.0, + -3.0 + ], + [ + -3.0, + 3.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2701,16 +2958,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.017986209962091562, 0.017986209962091562 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2728,14 +2985,27 @@ "receiver_port": "TASK_input_port_MappingProjection_from_task_input_OutputPort_0__to_TASK_InputPort_0_", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_10": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 1.0, + 0.0 + ], + [ + 0.0, + 1.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2750,16 +3020,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.0, 0.0 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2777,14 +3047,27 @@ "receiver_port": "color_hidden_input_port_MappingProjection_from_TASK_RESULT__to_color_hidden_InputPort_0_", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_11": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 4.0, + 4.0 + ], + [ + 0.0, + 0.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2799,16 +3082,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.5, 0.5 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2826,14 +3109,27 @@ "receiver_port": "word_hidden_input_port_MappingProjection_from_TASK_RESULT__to_word_hidden_InputPort_0_", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_13": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 0.0, + 0.0 + ], + [ + 4.0, + 4.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2848,16 +3144,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.5, 0.5 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2875,14 +3171,27 @@ "receiver_port": "DECISION_ARRAY", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_14": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 1.0, + 0.0 + ], + [ + 0.0, + 1.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2897,16 +3206,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.5, 0.5 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2924,14 +3233,27 @@ "receiver_port": "Conflict_Monitor_Value_of_OUTPUT__OutputPort_0_", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_1": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 1.0, + 0.0 + ], + [ + 0.0, + 1.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2946,16 +3268,16 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.5, 0.5 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } @@ -2973,14 +3295,22 @@ "receiver_port": "CONTROL_OUTCOME", "metadata": { "type": "MappingProjection", + "execute_until_finished": true, "max_executions_before_finished": 1000, "has_initializers": false, - "execute_until_finished": true, "exponent": null, "weight": null, "functions": { "LinearMatrix_Function_2": { - "function": "onnx::MatMul", + "function": { + "onnx::MatMul": { + "B": [ + [ + 1.0 + ] + ] + } + }, "args": { "B": [ [ @@ -2990,15 +3320,15 @@ }, "metadata": { "type": "LinearMatrix", - "max_executions_before_finished": 1000, - "changes_shape": false, - "has_initializers": false, + "output_type": "FunctionOutputType.DEFAULT", "A": [ 0.0 ], + "changes_shape": false, "execute_until_finished": true, + "max_executions_before_finished": 1000, + "has_initializers": false, "enable_output_type_conversion": false, - "output_type": "FunctionOutputType.DEFAULT", "bounds": null, "function_stateful_params": {} } diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 2b6cbdd614b..34a8cb0fbcc 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -3881,6 +3881,17 @@ def _mdf_metadata(self): } } + def _set_mdf_arg(self, model, arg, value): + # must set both args attr and args in function because otherwise + # mdf dependency tracking doesn't work + try: + if model.function is not None: + model.function[list(model.function.keys())[0]][arg] = value + except AttributeError: + pass + + model.args[arg] = value + @property def logged_items(self): """Dictionary of all items that have entries in the log, and their currently assigned `ContextFlags`\\s diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index 070cfb30508..cda41d037bf 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -897,7 +897,7 @@ def as_mdf_model(self): if typ not in mdf_functions.mdf_functions: warnings.warn(f'{typ} is not an MDF standard function, this is likely to produce an incompatible model.') - model.function = typ + model.function = {typ: parameters[self._model_spec_id_parameters]} return model diff --git a/psyneulink/core/components/functions/nonstateful/distributionfunctions.py b/psyneulink/core/components/functions/nonstateful/distributionfunctions.py index 565fdaa112a..91b255b14d4 100644 --- a/psyneulink/core/components/functions/nonstateful/distributionfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/distributionfunctions.py @@ -53,7 +53,7 @@ class DistributionFunction(Function_Base): def as_mdf_model(self): model = super().as_mdf_model() - model.args['shape'] = self.defaults.variable.shape + self._set_mdf_arg(model, 'shape', self.defaults.variable.shape) return model diff --git a/psyneulink/core/components/functions/nonstateful/transferfunctions.py b/psyneulink/core/components/functions/nonstateful/transferfunctions.py index c1dcb5cb684..ebb2bbc9b74 100644 --- a/psyneulink/core/components/functions/nonstateful/transferfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/transferfunctions.py @@ -1044,9 +1044,10 @@ def derivative(self, input=None, output=None, context=None): def as_mdf_model(self): model = super().as_mdf_model() + # x_0 is included in bias in MDF logistic - model.args['bias'] = model.args['bias'] - model.args['x_0'] - model.args['x_0'] = 0 + self._set_mdf_arg(model, 'bias', model.args['bias'] - model.args['x_0']) + self._set_mdf_arg(model, 'x_0', 0) if model.args['scale'] != 1.0: warnings.warn( diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index 35098ad6924..176eff725c7 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -708,7 +708,6 @@ def as_mdf_model(self): if ext_function_str is not None: model.metadata['custom_function'] = ext_function_str - model.function = ext_function_str del model.metadata['type'] return model diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index dbe87072fdc..778eaa033b2 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1098,7 +1098,7 @@ REMOVE_PORTS, PORT_SPEC, _parse_port_spec, PORT_SPECIFIC_PARAMS, PROJECTION_SPECIFIC_PARAMS from psyneulink.core.components.shellclasses import Mechanism, Projection, Port from psyneulink.core.globals.context import Context, ContextFlags, handle_external_context -from psyneulink.core.globals.json import _get_variable_parameter_name +from psyneulink.core.globals.json import _get_variable_parameter_name, _substitute_expression_args # TODO: remove unused keywords from psyneulink.core.globals.keywords import \ ADDITIVE_PARAM, EXECUTION_PHASE, EXPONENT, FUNCTION_PARAMS, \ @@ -4126,14 +4126,15 @@ def as_mdf_model(self): ) ) combination_function_id = f'{parse_valid_identifier(self.name)}_{MODEL_SPEC_ID_INPUT_PORT_COMBINATION_FUNCTION}' + combination_function_args = { + 'data': "combination_function_input_data", + 'axes': 0 + } model.functions.append( mdf.Function( id=combination_function_id, - function='onnx::ReduceSum', - args={ - 'data': "combination_function_input_data", - 'axes': 0 - } + function={'onnx::ReduceSum': combination_function_args}, + args=combination_function_args ) ) combination_function_dimreduce_id = f'{combination_function_id}_dimreduce' @@ -4168,9 +4169,14 @@ def as_mdf_model(self): else: primary_function_input_name = model.input_ports[0].id - function_model.args[_get_variable_parameter_name(self.function)] = primary_function_input_name + self.function._set_mdf_arg( + function_model, _get_variable_parameter_name(self.function), primary_function_input_name + ) model.functions.append(function_model) + for func_model in model.functions: + _substitute_expression_args(func_model) + return model diff --git a/psyneulink/core/components/mechanisms/processing/integratormechanism.py b/psyneulink/core/components/mechanisms/processing/integratormechanism.py index 5ab4fa8cf01..4da4319a3bc 100644 --- a/psyneulink/core/components/mechanisms/processing/integratormechanism.py +++ b/psyneulink/core/components/mechanisms/processing/integratormechanism.py @@ -89,6 +89,7 @@ from psyneulink.core.components.functions.stateful.integratorfunctions import AdaptiveIntegrator from psyneulink.core.components.mechanisms.processing.processingmechanism import ProcessingMechanism_Base from psyneulink.core.components.mechanisms.mechanism import Mechanism +from psyneulink.core.globals.json import _substitute_expression_args from psyneulink.core.globals.keywords import \ DEFAULT_VARIABLE, INTEGRATOR_MECHANISM, VARIABLE, PREFERENCE_SET_NAME from psyneulink.core.globals.parameters import Parameter @@ -254,4 +255,7 @@ def as_mdf_model(self): model.functions.extend(extra_noise_functions) function_model.args['noise'] = main_noise_function.id + for func_model in model.functions: + _substitute_expression_args(func_model) + return model diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index eaad3d4bd7b..44adbd44596 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -842,7 +842,7 @@ from psyneulink.core.components.ports.inputport import InputPort from psyneulink.core.components.ports.outputport import OutputPort from psyneulink.core.globals.context import ContextFlags, handle_external_context -from psyneulink.core.globals.json import _get_variable_parameter_name +from psyneulink.core.globals.json import _get_variable_parameter_name, _substitute_expression_args from psyneulink.core.globals.keywords import \ COMBINE, comparison_operators, EXECUTION_COUNT, FUNCTION, GREATER_THAN_OR_EQUAL, \ CURRENT_VALUE, LESS_THAN_OR_EQUAL, MAX_ABS_DIFF, \ @@ -1820,9 +1820,18 @@ def as_mdf_model(self): if self.defaults.integrator_mode: integrator_function_model = self.integrator_function.as_mdf_model() + primary_input = function_model.args[_get_variable_parameter_name(self.function)] - integrator_function_model.args[_get_variable_parameter_name(self.function)] = primary_input - function_model.args[_get_variable_parameter_name(self.function)] = integrator_function_model.id + self.integrator_function._set_mdf_arg( + integrator_function_model, + _get_variable_parameter_name(self.integrator_function), + primary_input + ) + self.function._set_mdf_arg( + function_model, + _get_variable_parameter_name(self.function), + integrator_function_model.id + ) for _, func_param in integrator_function_model.metadata['function_stateful_params'].items(): model.parameters.append(mdf.Parameter(**func_param)) @@ -1838,6 +1847,12 @@ def as_mdf_model(self): main_noise_function.id = f'{model.id}_{main_noise_function.id}' model.functions.append(main_noise_function) model.functions.extend(extra_noise_functions) - integrator_function_model.args['noise'] = main_noise_function.id + + self.integrator_function._set_mdf_arg( + integrator_function_model, 'noise', main_noise_function.id + ) + + for func_model in model.functions: + _substitute_expression_args(func_model) return model diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 16a80866908..62893463a6f 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -11487,7 +11487,7 @@ def is_included_projection(proj): graph = mdf.Graph( id=self_identifier, conditions=self.scheduler.as_mdf_model(), - **self._mdf_model_parameters, + **self._mdf_model_parameters[self._model_spec_id_parameters], **metadata ) diff --git a/psyneulink/core/globals/json.py b/psyneulink/core/globals/json.py index 5b934b3e1e4..4c598cc8c4d 100644 --- a/psyneulink/core/globals/json.py +++ b/psyneulink/core/globals/json.py @@ -173,6 +173,13 @@ def _get_variable_parameter_name(obj): return MODEL_SPEC_ID_MDF_VARIABLE +def _substitute_expression_args(model): + # currently cannot use args with value expressions + if model.value is not None: + for arg, val in model.args.items(): + model.value = model.value.replace(arg, str(val)) + + def _parse_component_type(component_dict): def get_pnl_component_type(s): from psyneulink.core.components.component import ComponentsMeta @@ -207,6 +214,16 @@ def get_pnl_component_type(s): except TypeError: # actually a str type_str = type_dict + elif isinstance(type_str, dict): + if len(type_str) != 1: + raise PNLJSONError + else: + elem = list(type_str.keys())[0] + # not a function_type: args dict + if MODEL_SPEC_ID_METADATA in type_str[elem]: + raise PNLJSONError + else: + type_str = elem try: # gets the actual psyneulink type (Component, etc..) from the module @@ -251,6 +268,11 @@ def _parse_parameter_value(value, component_identifiers=None, name=None, parent_ component_identifiers = {} exec('import numpy') + try: + pnl_type = _parse_component_type(value) + except (KeyError, TypeError, PNLJSONError): + # ignore parameters that aren't components + pnl_type = None if isinstance(value, list): new_val = [_parse_parameter_value(x, component_identifiers, name, parent_parameters) for x in value] @@ -303,7 +325,7 @@ def _parse_parameter_value(value, component_identifiers=None, name=None, parent_ name, parent_parameters ) - elif MODEL_SPEC_ID_PARAMETER_VALUE in value: + elif MODEL_SPEC_ID_PARAMETER_VALUE in value and pnl_type is None: # is a standard mdf Parameter class with value value = _parse_parameter_value( value[MODEL_SPEC_ID_PARAMETER_VALUE], @@ -486,7 +508,7 @@ def _generate_component_string( try: parameters = dict(component_dict[component_type._model_spec_id_parameters]) except KeyError: - pass + parameters = {} parameters['custom_function'] = f'{custom_func}' try: del component_dict[MODEL_SPEC_ID_METADATA]['custom_function'] @@ -498,6 +520,12 @@ def _generate_component_string( except KeyError: pass + try: + # args in function dict + parameters.update(component_dict['function'][list(component_dict['function'].keys())[0]]) + except (AttributeError, KeyError): + pass + parameter_names = {} # If there is a parameter that is the psyneulink identifier string diff --git a/requirements.txt b/requirements.txt index 088c7d7ec7d..42b0dadb2ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ grpcio<1.43.0 grpcio-tools<1.43.0 llvmlite<0.39 matplotlib<3.5.2 -modeci_mdf<0.3.2 +modeci_mdf>=0.3.2, <0.3.4 networkx<2.8 numpy<1.21.4, >=1.17.0 pillow<9.1.0 From 8fa3011b8ebf291858911424fd8c90d6e482575d Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sat, 19 Mar 2022 20:41:16 -0400 Subject: [PATCH 249/285] Refactor/ocm/state features all as input ports (#2351) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Composition: add_controller: set METHOD as context source early * - * • composition.py retore append of control_signals in _instantiate_control_projections() * • composition.py restore append of control_signals in _instantiate_control_projections() • test_composition.py: add test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp * • test_partially_overlapping_local_and_control_mech_control_specs_in_unnested_and_nested_comp(): - added clear_registry() to allow names to be reused in both runs of test * • composition.py docstring: added projections entry to list of attributes - add_controller: added call to _add_node_aux_components() for controller * • composition.py _add_node_aux_components(): added deletion of item from aux_components if instantiated * • composition.py - comment out _add_node_aux_components() (causing new failures) - move _instantiate_control_projections to be with _instantiate_control_projections, after self.add_node(self.controller.objective_mechanism (to be more orderly) * - * - confirm that it passes all tests exception test_composition/test_partially_overlapping... (with addition of _add_aux_components in add_controller commented out) * • composition.py: some more fixed to add_controller that now fail only one test: - test_agent_rep_assignement_as_controller_and_replacement * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * - * - * - * - * - * - * - * - * • inputport.py: - add figure for shadowing * - * - * • processingmechanism.py: - __init__(): modify typecheck for input_ports to allow single items * • optimizationcontrolmechanism.py - allow single spec (None, array, tuple, or Components) that is assigned to all INPUT Node InputPorts - state_feature_default is assigned to all unspecified INPUT Node InputPorts (for a list that is shorter than the number, a dict or a set that has fewer, or any that are added to agent_rep after controller is initially constructed and added to Composition) * - * - * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * - * - * - * - * - Co-authored-by: Katherine Mantel Co-authored-by: jdcpni --- .../control/optimizationcontrolmechanism.py | 657 ++++++++---------- .../processing/processingmechanism.py | 6 +- psyneulink/core/components/ports/inputport.py | 4 +- psyneulink/core/compositions/composition.py | 39 +- tests/composition/test_control.py | 156 +++-- 5 files changed, 406 insertions(+), 456 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 410a3d2533b..f1f157af73c 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -59,7 +59,7 @@ by using the `OptimizationFunction` (assigned as its `function `) to execute its `agent_rep ` -- a representation of the Composition to be optimized -- under different `control_allocations `, and selecting the one that optimizes -its `net_outcome `. A OptimizationControlMechanism can be configured to implement +its `net_outcome `. An OptimizationControlMechanism can be configured to implement various forms of optimization, ranging from fully `model-based optimization ` that uses the Composition itself as the `agent_rep ` to simulate the outcome for a given `state ` (i.e., a combination of the current input and a @@ -213,17 +213,20 @@ .. _OptimizationControlMechanism_State_Features_Arg: -* **state_features** -- specifies the values provided by the OptimizationControlMechanism as the input to its - `agent_rep `\\'s `evaluate ` method, together - with a selected `control_allocation `, when that is called to estimate - or predict the Composition's `net_outcome `. These are used to construct the - `state_input_ports ` for the OptimizationControlMechanism, - the `values ` of which are assigned as the `state_feature_values - ` and provided to the **predicted_inputs** argument of the - `evaluate ` method if `agent_rep ` is a `Composition`, - or the **feature_values** argument if it is a `CompositionFunctionApproximator`. Accordingly, the specification - requirements for **state_features** depend on whether the `agent_rep` is a - `Composition` or a `CompositionFunctionApproximator`, as described in each of the two sections below. +* **state_features** -- specifies the sources of input to the OptimizationControlMechanism's `agent_rep + ` which, together with a selected `control_allocation + `, are provided as input to it's `evaluate ` method + when that is executed to estimate or predict the Composition's `net_outcome `. + Those sources of input are used to construct the OptimizationControlMechanism's `state_input_ports + `, one for each external `InputPort` (i.e., that is not designated + as `internal_only `) of each `INPUT ` `Node ` of the + `agent_rep `. The input to each `state_input_port + `, after being processed by it `function `, is + assigned as the corresponding value of `state_feature_values `, + the values of which provided as the input to the corresponding InputPorts of the `INPUT ` `Nodes + of the agent_rep each time it is `evaluated `. Accordingly, the specification requirements + for **state_features** depend on whether the `agent_rep` is a `Composition` + or a `CompositionFunctionApproximator`, as described in each of the two sections below. | @@ -235,38 +238,34 @@ .. _OptimizationControlMechanism_State_Features_Automatic_Assignment: - *Automatic assignment.* The **state_features** specify the inputs to the Composition assigned as the `agent_rep - ` when it is executed by the OptimizationControlMechanism to - `evaluate ` its performance. The default is for the evaluation to use the - same values received by the `agent_rep ` as its `external inputs - ` during its last `TRIAL ` of execution. Accordingly, if - **state_features** is not specified, a set of `state_input_ports ` - is constructed automatically that `shadow the input ` to every `InputPort` of every - `INPUT ` `Node ` of the `agent_rep ` - Composition. + *Automatic assignment.* By default, if **state_features**, **state_feature_default** and **state_feature_function** + are not specified, the `state_input_ports ` are configured to + `shadow the inputs ` of every external `InputPort` (i.e., all of the ones not designated + as `internal_only `) of every `INPUT ` `Node ` of the + `agent_rep ` Composition; as a result, each time `agent_rep + ` is `evaluated `, it receives the same `external + input `) it received during its last `TRIAL` of execution. | .. _OptimizationControlMechanism_State_Features_Explicit_Specification: - *Explicit specification.* The **state_features** argument can also be specified explicitly, using the formats - described below. This is useful if: values other than the `external inputs ` to - the `agent_rep ` Composition are to be used when it is evaluated; to restrict - evaluation to a subset of its inputs (while others are held constant); and/or to assign specific functions to one or - more `state_input_ports ` (see `below + *Explicit specification.* Specifying the **state_features**, **state_feature_default** and/or + **state_feature_function** arguments explicitly can be useful if: values need to be provided as input to the + `agent_rep ` when it is evaluated other than its `external inputs + `; to restrict evaluation to a subset of its inputs (while others are held constant); + and/or to assign specific functions to one or more `state_input_ports + ` (see `below `) that allow them to process the inputs (e.g., modulate and/or integrate them) before they are assigned to `state_feature_values - `. Note that assigning *any* **state_features** explicitly - overrides their automatic assignment; any that are *not* specified will be assigned the `state_feature_default - `. By default, `state_feature_default - ` is ``None``, which means any InputPort of an `INPUT - ` for which no state_feature is specified will be provided with the value of its `default_variable - ` when the `agent_rep `\\'s - `evaluate ` method is executed. However, `state_feature_default - ` can be set to other values (e.g., SHADOW_INPUTS), that is then - used for any unspecified state_features (e.g., `state_input_ports ` - are constructed that `shadow their inputs `, as in the case of `automatic assignment - `). + ` and passed to the `agent_rep + `. Assignments can be made to **state_features** corresponding + to any or all InputPorts of the `agent_rep `\\'s `INPUT ` + `Nodes `, as described `below `. + Any that are not specified are assigned the value specified for **state_feature_default** (*SHADOW_INPUTS* by + default; see `state_feature_default ` for additional details). + A single assignment can be made for all **state_features**, or they can be specified individually for each `INPUT + ` `Nodes ` InputPort, as descdribed below. .. _OptimizationControlMechanism_State_Features_Shapes: @@ -280,146 +279,124 @@ ` attribute. A failure to properly meet these requirements produces an error. - COMMENT: - .. _OptimizationControlMechanism_Selective_Input: - - .. hint:: - For cases in which only a subset of the inputs to the Composition are relevant to its optimization (e.g., - the others should be held constant), it is still the case that all must be specified as **state_features** - (see note above). This can be handled several ways. One is by specifying (as required) **state_features** - for all of the inputs, and assigning *state_feature_function** (see `below - `) such that those assigned to the desired - inputs pass their values unmodified, while those for the inputs that are to be ignored return a constant value. - Another approach, for cases in which the desired inputs pertain to a subset of Components in the Composition - solely responsible for determining its `net_outcome `, is to assign those - Components to a `nested Composition ` and assign that Composition as the `agent_rep - `. A third, more sophisticated approach, is to assign - ControlSignals to the InputPorts for the irrelevant features, and specify them to suppress their values. - COMMENT - - .. _OptimizationControlMechanism_State_Features_Shadow_Inputs: + .. _OptimizationControlMechanism_State_Features_Specification: The **state_features** argument can be specified using any of the following formats: - .. _Optimization_Control_Mechanism_State_Feature_Input_Dict: - - * *Inputs dictionary* -- must conform to the format used to `specify external inputs ` - to the `agent_rep `, in which entries consist of a key specifying an `INPUT - ` Node of `agent_rep `, and its value is the source of the - input, that can be any of the forms of individual input specifications listed `below - `. This is the most straightforward and reliable - way to specify **state_features**. The full format required for inputs to `agent_rep - ` can be seen using its `get_input_format ` - method. If any `INPUT ` Nodes are not specified or assigned None as their value, their `default - variable ` is used for their input when the `agent_rep - `\\'s `evaluate ` method is executed, irrespective - of the input to the `agent_rep ` during the last `TRIAL `. - If a nested Composition is specified (that is an `INPUT ` Node of `agent_rep - `), the value assigned to it is used for *all* of the `INPUT - ` Nodes for the nested Composition and any nested within it, at all levels of nesting. If one or - more `INPUT ` Nodes of a nested Composition (that are INPUT Nodes at all levels of nesting) are - specified, then any unspecified INPUT Nodes of the corresponding Compositions are assigned None as their values. - - .. _Optimization_Control_Mechanism_State_Feature_List_Inputs: - - * *List* -- a list of individual input source specifications, that can be any of the forms of individual - input specifications listed `below `. + .. _OptimizationControlMechanism_State_Feature_Input_Dict: + + * *Single specification* -- any of the indivdiual specifications described `below + ` can be directly to **state_features**, that is + then used to construct *all* of the `state_input_ports `, one + for each external InputPort (i.e., ones not designated as `internal_only `) of each + `INPUT ` `Node ` of the `agent_rep `. + + * *Inputs dictionary* -- specifies state_features (entry values) for individual `InputPorts ` and/or + `INPUT ` `Nodes ` of the `agent_rep ` + (entry keys). It must conform to the format used to `specify external inputs ` to the + `agent_rep `, in which entries consist of a key specifying either an `INPUT` + ` `Node ` of the `agent_rep ` or one of + their external `InputPorts `, and a value that is the source of the input that can be any of the forms of + individual input specifications listed `below `. The + format required for the entries can be seen using either the `agent_rep ` + `get_input_format ` method (for inputs to its `INPUT ` `) or its `external_input_ports_of_all_input_nodes + ` (for all of their external InputPorts). If a nested + Composition is specified (that is, one that is an `INPUT ` Node of `agent_rep + `), the state_feature assigned to it is used to construct the + `state_input_ports ` for *all* of the external InputPorts of all of + the`INPUT ` Nodes for that nested Composition, and any nested within it at all levels of nesting. + If any `INPUT ` Nodes or their InputPorts are not specified in the dictionary, the + `state_feature_default ` is assigned as their state_feature + (this includes cases in which some but not all `INPUT ` Nodes of a nested Composition, or their + InputPorts, are specified; any unspecified INPUT Nodes of the corresponding Compositions are assigned + `state_feature_default ` as their state_feature). + + .. _OptimizationControlMechanism_State_Feature_List_Inputs: + + * *List* -- a list of individual state_feature specifications, that can be any of the forms of individual + input specifications listed `below `. The items correspond to all of the external InputPorts (i.e., ones not designated as `internal_only `) of all of the `INPUT ` Nodes` of the `agent_rep `, and must be specified in the order they are listed in the `agent_rep `\\'s `external_input_ports_of_all_input_nodes ` attribute. If the list is incomplete, the remaining - InputPorts are assigned the `state_feature_default `; - ``None`` can also be used as an entry in the list to "skip" that item (i.e., specify that it is assigned the - `state_feature_default `. If the `state_feature_default - ` is ``None`` (*its* default value), then no `state_input_port - ` is constructed for that source, and the InputPort of the - `INPUT ` `Node ` will be assigned the value of its `default variable - ` as its input when the `agent_rep `\\'s `evaluate - ` method is executed. Items can be included in the list that have not yet been added to the - OptimizationControlMechanism's Composition or its `agent_rep `, that are - either sources of input to `agent_rep `\\'s `INPUT ` - `Nodes `, or those Nodes themselves. However, these must be added before the Composition is - executed, and must appear in the list in the same position that the InputPorts to which they pertain are listed + InputPorts are assigned the `state_feature_default `. + Items can be included in the list that have not yet been added to the OptimizationControlMechanism's Composition + or its `agent_rep `. However, these must be added before the Composition + is executed, and must appear in the list in the same position that the InputPorts to which they pertain are listed in the `agent_rep `\\'s `external_input_ports_of_all_input_nodes ` attribute, once construction of the `agent_rep ` is complete. - .. _Optimization_Control_Mechanism_State_Feature_Set_Inputs: + .. _OptimizationControlMechanism_State_Feature_Set_Inputs: * *Set* -- a set of `INPUT ` `Nodes ` of the `agent_rep - ` to receive the same inputs during evaluation as when the `agent_rep - ` is fully executed; the `state_input_ports - ` constructed for these state_features are assigned Projections - that `shadow ` the inputs to the specified `INPUT ` `Nodes - ` of the `agent_rep `. The order of their - specification does not matter; however, any of the `agent_rep `\\'s - `INPUT ` Nodes that are *not* included in the set will be assigned the `state_feature_default - ` if it is specified; otherwise, they are assigned their - `default variable ` when the `agent_rep `\\'s - `evaluate ` method is executed. - - .. _Optimization_Control_Mechanism_State_Feature_Individual_Inputs: - - * *Individual inputs* -- any of the forms below can be used singly, or in a dict or list as described - `above `, to configure a `state_input_port - `, the value of which is assigned as the corresponding entry of - `state_feature_values ` and provided as input to the - corresponding InputPort of the `agent_rep `\\'s `INPUT ` - `Nodes ` when it's `evaluate ` method is executed. - - .. note:: - If only a single input specification is provided to **state_features**, it is treated as a list with a single - item (see `above `), and assigned as the input to the - first `INPUT ` Node of `agent_rep `; if the latter has - any additional `INPUT ` Nodes, they are assigned their `default variable ` - as inputs when the `agent_rep `\\'s `evaluate ` - method is executed. - - .. _Optimization_Control_Mechanism_Numeric_State_Feature: - - * *numeric value* -- create an `InputPort` with the specified value as its `default variable ` - and no `afferent Projections `; as a result, the specified value is assigned as the - input to the corresponding `INPUT ` `Node ` of the `agent_rep - ` each time it is `evaluated `. - - .. _Optimization_Control_Mechanism_SHADOW_INPUTS_State_Feature: - - * *SHADOW_INPUTS* -- create an `InputPort` that `shadows the input ` of the InputPort - to which the specification is assigned. - - .. _Optimization_Control_Mechanism_Tuple_State_Feature: - - * *2-item tuple* -- the first item must be a `Port` or `Mechanism` specification, as described below; - the second item must be a `Function`, that is assigned as the `function ` of the - corresponding `state_input_port `; - this takes precedence over any other state_feature_function specifications (e.g., in an `InputPort - specification dictionary ` or the **state_feature_function** argument - of the OptimizationControlMechanism's constructor; see `state_feature_function - ` for additional details). - - .. _Optimization_Control_Mechanism_Input_Port_Dict_State_Feature: - - * *specification dictionary* -- an `InputPort specification dictionary ` - can be used to configure the corresponding `state_input_port `, - if `Parameters ` other than its `function ` need to be specified (e.g., its `name - ` or more than a single `afferent Projection `). - - .. _Optimization_Control_Mechanism_Input_Port_State_Feature: - - * *InputPort specification* -- creates an `InputPort` that `shadows ` the input to - the specified InputPort, the `value ` of which is assigned as the corresponding value of the - OptimizationControlMechanism's `state_feature_values `. + ` that are assigned *SHADOW_INPUTS* as their state_feature (see `below + <_OptimizationControlMechanism_SHADOW_INPUTS_State_Feature>`); that is, that should receive the same inputs + during evaluation as when the Composition of which the OptimizationControlMechanism is the `controller + ` is fully executed. The order of their specification does not matter; however, any of + the `agent_rep `\\'s `INPUT ` Nodes that are *not* included + in the set will be assigned the `state_feature_default `. + + .. _OptimizationControlMechanism_State_Feature_Individual_Specs: + + * *Individual state_feature specifications* -- any of the specifications listed below can be used singly, + or in a dict, list or set as described `above `, + to configure `state_input_ports `. + + .. _OptimizationControlMechanism_Numeric_State_Feature: + + * *None* -- no `state_input_port ` is constructed for the + corresponding `INPUT ` `Node ` InputPort, and its `default variable + ` is assigned directly to the corresponding value of `state_feature_values + `, and therefore as the input to that InputPort whenever the + ` is `evaluated `, irrespective of its input when + the `agent_rep ` was last executed. + + * *numeric value* -- create a `state_input_port ` with the specified + value as its `default variable ` and no `afferent Projections `; + as a result, the specified value is assigned as the input to the corresponding value of `state_feature_values + `, and the coreresponding `INPUT ` `Node + ` InputPort each time the `agent_rep ` is `evaluated + `. + + .. _OptimizationControlMechanism_SHADOW_INPUTS_State_Feature: + + * *SHADOW_INPUTS* -- create a `state_input_port ` that `shadows + the input ` of the InputPort to which the specification is assigned; that is, each time + `agent_rep ` is `evaluated `, the state_input_port + receives the same input that the corresponding `INPUT ` `Node ` InputPort + received during the last `TRIAL ` of execution. + + .. _OptimizationControlMechanism_Input_Port_State_Feature: + + * *InputPort specification* -- create a `state_input_port ` that + `shadows ` the input to the specified `InputPort`; that is, each time `agent_rep + ` is `evaluated `, the state_input_port receives + the same input that the specified InputPort received during the last `TRIAL ` in which the + Composition for which the OptimizationControlMechanism is the `controller ` was executed. + The specification can be any form of `InputPort specification ` for the `InpuPort` + of any `Mechanism ` that is an `INPUT ` `Node ` in the Composition + (not limited to the `agent_rep `). This includes an + `InputPort specification dictionary `, that can be used to configure the + corresponding `state_input_port `, if `Parameters ` + other than its `function ` need to be specified (which can be done directly using a + `2-item tuple ` specification or the **state_feature_function** + arg as described `below `), such as the InputPort's + `name ` or more than a single `afferent Projection `. + + .. _OptimizationControlMechanism_INPUT_Node_Specification: .. note:: Only the `INPUT ` `Nodes ` of a `nested Composition ` - can shadowed. Therefore, if the Composition that an OptimizationControlMechanism controls contains any - nested Compositions, only its `INPUT ` Nodes can be specified for shadowing in the - **state_features** argument of the OptimizationControlMechanism's constructor. + can be shadowed. Therefore, if the Composition that an OptimizationControlMechanism controls contains any + nested Compositions, only its `INPUT ` `Nodes ` can be specified for + shadowing in the **state_features** argument of the OptimizationControlMechanism's constructor. .. hint:: - Shadowing the input to a Node of a `nested Composition ` that is not an `INTERNAL - ` Node of that Composition can be accomplished one or of two ways, by: a) assigning it + Shadowing the input to a Node of a `nested Composition ` that is not an `INPUT + ` Node of that Composition can be accomplished in one or of two ways, by: a) assigning it `INPUT ` as a `required NodeRole ` where it is added to the nested Composition; and/or b) adding an additional Node to that Composition that shadows the desired one (this is allowed *within* the *same* Composition), and is assigned as an `OUTPUT ` Node of @@ -429,24 +406,38 @@ .. technical_note:: The InputPorts specified as state_features are designated as `internal_only ` = `True`. - .. _Optimization_Control_Mechanism_Output_Port_State_Feature: - - * *OutputPort specification* -- this creates an `InputPort` that receives a `MappingProjection` from the - specified `OutputPort`; it can be any form of `OutputPort specification ` - for any `OutputPort` of another `Mechanism ` in the Composition. The `value ` - of the specified OutputPort is used as the corresponding value of the OptimizationControlMechanism's - `state_feature_values `. - - .. _Optimization_Control_Mechanism_Mechanism_State_Feature: - - * *Mechanism* -- if the `agent_rep ` is a Composition, the Mechanism's - `primary InputPort ` is shadowed; that is, it is assumed that its' input should be used - as the corresponding value of the OptimizationControlMechanism's `state_feature_values - `. This has the same result as explicitly specifying the - Mechanism's input_port, as described `above `. If - the Mechanism is in a `nested Composition `, it must be an `INPUT ` `Node - ` of that Composition (see note above). If its OutputPort needs to be used, it must be - specified explicitly (as described `above `). + .. _OptimizationControlMechanism_Output_Port_State_Feature: + + * *OutputPort specification* -- create a `state_input_port ` that + receives a `MappingProjection` from the specified `OutputPort`; that is, each time `agent_rep + ` is `evaluated `, the state_input_port receives + the `value ` of the specified OutputPort after the last `TRIAL ` in which the + Composition for which the OptimizationControlMechanism is the `controller ` was executed. + The specification can be any form of `OutputPort specification ` for any `OutputPort` + of a `Mechanism ` in the Composition (not limited to the `agent_rep + `. + + .. _OptimizationControlMechanism_Mechanism_State_Feature: + + * *Mechanism* -- create a `state_input_port ` that `shadows + ` the input to the `primary InputPort ` of the specified Mechanism + (this is the same as explicitly specifying the Mechanism's input_port, as described `above + `). If the Mechanism is in a `nested Composition + `, it must be an `INPUT ` `Node ` of that Composition + (see `note ` above). If the Mechanism's `OutputPort` + needs to be used, it must be specified explicitly (as described `above + `). + + .. _OptimizationControlMechanism_Tuple_State_Feature: + + * *2-item tuple* -- the first item must be any of the forms of individual state_feature specifications + described `above `, and the second + item must be a `Function`, that is assigned as the `function ` of the corresponding + `state_input_port `; this takes precedence over + any other state_feature_function specifications (e.g., in an `InputPort specification dictionary + ` or the **state_feature_function** argument of the + OptimizationControlMechanism's constructor; see `state_feature_function + ` for additional details). | @@ -470,13 +461,13 @@ The list of specifications can contain any of the forms of specification used for an `agent_rep ` that is a Composition as described `above - `, with the following exception: if a + `, with the following exception: if a `Mechanism` is specified, its `primary OutputPort ` is used (rather than shadowing its primary InputPort), since that is more typical usage, and there are no assumptions made about the state features of a `CompositionFunctionApproximator` (as there are about a Composition as `agent_rep `); if the input to the Mechanism *is* to be `shadowed `, then its InputPort must be specified explicitly (as described - `above `). + `above `). | @@ -486,7 +477,7 @@ function for `state_input_ports `. This is assigned as the `function ` to any state_input_ports for which no other `Function` is specified; i.e., in an InputPort specification dictionary ` or `2-item tuple - ` in the **state_features** argument (see `state_features + ` in the **state_features** argument (see `state_features `). If either of the latter is specified, they override the specification in **state_feature_function**. If it is *not* specified, then `LinearCombination` (the standard default `Function` for an `InputPort`) is assigned to any `state_input_ports @@ -653,7 +644,7 @@ ` in the `Composition` for which the OptimizationControlMechanism is a `controller ` (irrespective of whether that is the OptimizationControlMechanism`s `agent_rep `). They are implemented as `shadow InputPorts ` - (see `OptimizationControlMechanism_State_Features_Shadow_Inputs` for specification) that receive a `Projection` + (see `OptimizationControlMechanism_SHADOW_INPUTS_State_Feature` for specification) that receive a `Projection` from the same source as the Mechanism being shadowed. .. * *Output Features* -- these are the `value ` of an `OutputPort` of a `Mechanism ` in @@ -1586,13 +1577,13 @@ class Parameters(ControlMechanism.Parameters): state_feature_specs see `state_feature_specs ` - :default value: None + :default value: SHADOW_INPUTS :type: ``dict`` state_feature_default see `state_feature_default ` - :default value: None + :default value: SHADOW_INPUTS :type: state_feature_function @@ -1609,9 +1600,9 @@ class Parameters(ControlMechanism.Parameters): """ agent_rep = Parameter(None, stateful=False, loggable=False, pnl_internal=True, structural=True) state_input_ports = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) - state_feature_specs = Parameter(None, stateful=False, loggable=False, read_only=True, + state_feature_specs = Parameter(SHADOW_INPUTS, stateful=False, loggable=False, read_only=True, structural=True, parse_spec=True) - state_feature_default = Parameter(None, stateful=False, loggable=False, read_only=True) + state_feature_default = Parameter(SHADOW_INPUTS, stateful=False, loggable=False, read_only=True) state_feature_function = Parameter(None, reference=True, stateful=False, loggable=False) state_feature_values = Parameter(None,getter=_state_feature_values_getter, user=False, pnl_internal=True, read_only=True) @@ -1641,18 +1632,7 @@ class Parameters(ControlMechanism.Parameters): saved_samples = None saved_values = None - def _parse_state_feature_specs(self, state_features): - # return (state_features if isinstance(state_features, (dict, set)) else convert_to_list(state_features)) - from psyneulink.core.compositions.composition import Composition - return (state_features if (isinstance(state_features, set) - or (isinstance(state_features, dict) - and (any(isinstance(key, (Port, Mechanism, Composition)) - for key in state_features) - or SHADOW_INPUTS in state_features))) - else convert_to_list(state_features)) - def _validate_state_feature_default(self, state_feature_default): - # FIX: 3/16/22 - from psyneulink.core.compositions.composition import Composition if not (isinstance(state_feature_default, (InputPort, OutputPort, Mechanism)) or state_feature_default == SHADOW_INPUTS or is_numeric(state_feature_default) @@ -1664,7 +1644,7 @@ def _validate_state_feature_default(self, state_feature_default): @tc.typecheck def __init__(self, agent_rep=None, - state_features: tc.optional(tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort)))=None, + state_features: tc.optional((tc.any(str, Iterable, InputPort, OutputPort, Mechanism)))=SHADOW_INPUTS, state_feature_default=None, state_feature_function: tc.optional(tc.optional(tc.any(dict, is_function_type)))=None, function=None, @@ -1716,6 +1696,7 @@ def __init__(self, # Store args for deferred initialization self._store_deferred_init_args(**locals()) self._init_args['state_feature_specs'] = state_features + self._init_args['state_feature_default'] = state_feature_default # Flag for deferred initialization self.initialization_status = ContextFlags.DEFERRED_INIT @@ -1816,7 +1797,7 @@ def _instantiate_input_ports(self, context=None): The OptimizationControlMechanism's outcome_input_ports are instantiated by ControlMechanism._instantiate_input_ports in the call to super(). - InputPorts are constructed for **state_features** by calling _complete_parsing_state_feature_specs + InputPorts are constructed for **state_features** by calling _parse_state_feature_specs() with them and **state_feature_function** arguments of the OptimizationControlMechanism constructor. The constructed state_input_ports are passed to ControlMechanism_instantiate_input_ports(), which appends them to the InputPort(s) that receive input from the **objective_mechanism* (if specified) @@ -1834,11 +1815,8 @@ def _instantiate_input_ports(self, context=None): `OptimizationControlMechanism_State_Input_Ports` for additional details. """ - # If any state_features were specified parse them and pass to ControlMechanism._instantiate_input_ports() - state_input_ports_specs = None - # FIX: 11/3/21 : - # ADD CHECK IN _complete_parsing_state_feature_specs THAT IF A NODE RATHER THAN InputPort IS SPECIFIED, + # ADD CHECK IN _parse_state_feature_specs() THAT IF A NODE RATHER THAN InputPort IS SPECIFIED, # ITS PRIMARY IS USED (SEE SCRATCH PAD FOR EXAMPLES) if not self.state_feature_specs: # If agent_rep is CompositionFunctionApproximator, warn if no state_features specified. @@ -1847,12 +1825,11 @@ def _instantiate_input_ports(self, context=None): if self.agent_rep_type == COMPOSITION_FUNCTION_APPROXIMATOR: warnings.warn(f"No '{STATE_FEATURES}' specified for use with `agent_rep' of {self.name}") - else: - # Implement any specified state_features - state_input_ports_specs = self._complete_parsing_state_feature_specs(context) - # Note: - # if state_features were specified and agent_rep is a CompositionFunctionApproximator, - # assume they are OK (no way to check their validity for agent_rep.evaluate() method, and skip assignment + # Implement any specified state_features + state_input_ports_specs = self._parse_state_feature_specs(context) + # Note: + # if state_features were specified and agent_rep is a CompositionFunctionApproximator, + # assume they are OK (no way to check their validity for agent_rep.evaluate() method, and skip assignment # Pass state_input_ports_sepcs to ControlMechanism for instantiation and addition to OCM's input_ports super()._instantiate_input_ports(state_input_ports_specs, context=context) @@ -1860,7 +1837,6 @@ def _instantiate_input_ports(self, context=None): # Assign to self.state_input_ports attribute start = self.num_outcome_input_ports # FIX: 11/3/21 NEED TO MODIFY IF OUTCOME InputPorts ARE MOVED stop = start + len(state_input_ports_specs) if state_input_ports_specs else 0 - # FIX 11/3/21: THIS SHOULD BE MADE A PARAMETER self.parameters.state_input_ports.set(ContentAddressableList(component_type=InputPort, list=self.input_ports[start:stop]), override=True) @@ -1933,15 +1909,14 @@ def _validate_input_nodes(self, nodes, enforce=None): items_str = f"contains items ({items}) that are" message = f"The '{STATE_FEATURES}' specified for '{self.name}' {items_str} not in its {AGENT_REP} " \ f"('{self.agent_rep.name}'). Executing '{self.agent_rep.name}' before " \ - f"{'they are' if singular else 'it is'} added will generate an error ." + f"{'it is' if singular else 'they are'} added will generate an error ." if enforce: raise OptimizationControlMechanismError(message) else: warnings.warn(message) # FIX: 1/29/22 - REFACTOR TO SUPPORT TUPLE AND InportPort SPECIFICATION DICT FOR MULT. PROJS. TO STATE_INPUT_PORT - # FIX: 2/25/22 - REFACTOR TO SUPPORT InputPort SPECIFICATIONS IN dict, set and list specs - def _complete_parsing_state_feature_specs(self, context=None): + def _parse_state_feature_specs(self, context=None): """Parse entries of state_features specifications used to construct state_input_ports. Called from _instantiate_input_ports() @@ -1965,50 +1940,53 @@ def _complete_parsing_state_feature_specs(self, context=None): Handle four formats: - - dict {INPUT Node: source or None, INPUT Node: source or None...}: - - every key must be an INPUT Node of agent_rep or an INPUT Node of a nested Composition within it that - is itself an INPUT Node of its enclosing Composition, at any level of nesting; - - if a Composition is specified as a key, construct a state_input_port for each InputPort of each of its - INPUT Nodes, and those of any Compositions nested within it at all levels of nesting, and assign the - the value of the dict entry as the source for all of them; + - dict {INPUT Node: source or None, INPUT Node or InputPort: source or None...}: + - every key must be an INPUT Node of agent_rep or an INPUT Node of a nested Composition within it that is + itself an INPUT Node of its enclosing Composition, or the external InputPort of one, at any level of + nesting; + - if a Mechanism is specified as a key, construct a state_input_port for each of its external InputPorts, + and assign the value of the dict entry as the source for all of them; + - if a Composition is specified as a key, construct a state_input_port for each external InputPort of each + of its INPUT Nodes, and those of any Compositions nested within it at all levels of nesting, + and assign the the value of the dict entry as the source for all of them; - for INPUT Nodes not specified or assigned None as their value, assign corresponding entries in - state_feature_specs as None, and don't construct a state_input_port for them; + state_feature_specs as state_feature_default - if only one or some of the INPUT Nodes of a nested Composition are specified, - for the remaining ones assign the corresponding entries in state_feature_specs as None, - and don't construct state_input_ports for them; - - list [source, None, source...]: specifies source specs for INPUT Nodes - - must be listed in same order as *expanded* list of agent_rep INPUT Nodes to which they correspond (i.e., - nested Compositions that are INPUT Nodes replaced by their INPUT Nodes, for all levels of nesting); - - if there are fewer sources listed than INPUT Nodes, assign None to the entries in state_feature_specs - corresponding to the remaining INPUT Nodes and don't construct state_input_ports for them; + for the remaining ones assign the corresponding entries in state_feature_specs as state_feature_default + - if None is specified, don't construct a state_input_port + - list [source, None, source...]: specifies source specs for INPUT Node external InputPorts: + - must be listed in same order as *expanded* list of agent_rep INPUT Node external InputPorts to which they + correspond (i.e., nested Compositions that are INPUT Nodes replaced by their INPUT Nodes, + for all levels of nesting); + - if there are fewer sources listed than INPUT Node external InputPorts, assign state_feature_default to + the entries in state_feature_specs corresponding to the remaining INPUT Node external InputPorts - if there more sources listed than INPUT Nodes, leave the excess ones, and label them as 'EXPECTED INPUT NODE n' for later resolution (see below). - set {INPUT Node, Input Node...}: specifies INPUT Nodes to be shadowed - every item must be an INPUT Node of agent_rep or an INPUT Node of a nested Composition within it that is itself an INPUT Node of its enclosing Composition, at any level of nesting; - - if a Composition is specified, construct a state_input_port for each of its INPUT Nodes, and those of - any Compositions nested within it at all levels of nesting, each of which shadows the input of the - corresponding INPUT Node (see _InputPort_Shadow_Inputs). - - if only one or some of the INPUT Nodes of a nested Composition are specified, don't state_input_ports - for the remaining ones, and assign them their default inputs in evaluate(). + - if a Composition is specified, construct a state_input_port for each of its INPUT Node extenal InputPorts, + and those of any Compositions nested within it at all levels of nesting, each of which shadows the + input of the corresponding INPUT Node (see _InputPort_Shadow_Inputs). + - if only one or some of the INPUT Nodes of a nested Composition are specified, use state_feature_default. IMPLEMENTATION NOTE: this is a legacy format for consistency with generic specification of shadowing inputs - SHADOW_INPUTS dict {"SHADOW_INPUTS":[shadowable input, None, shadowable input...]}: - - all items must be a Mechanism (or one of its InputPorts) that is an INPUT Node of agent_rep or + - all items must be a Mechanism (or one of its external InputPorts) that is an INPUT Node of agent_rep or of a nested Composition within it that is itself an INPUT Node; - must be listed in same order as *expanded* list of agent_rep INPUT Nodes to which they correspond (see list format above); - construct a state_input_port for each non-None spec, and assign it a Projection that shadows the spec. (see _InputPort_Shadow_Inputs). - If shadowing is specified for an INPUT Node, set INTERNAL_ONLY to True in entry of params dict in + If shadowing is specified for an INPUT Node InputPort, set INTERNAL_ONLY to True in entry of params dict in specification dictionary for corresponding state_input_port (so that inputs to Composition are not required if the specified source is itself an INPUT Node). - If an INPUT Node is specified that is not (yet) in agent_rep, and/or a source is specified that is not yet - in self.composition, warn and defer creating a state_input_port; final check is made, and error(s) - generated for unresolved specifications at run time. + If an INPUT Node (or one of its external InputPorts) is specified that is not (yet) in agent_rep, + and/or a source is specified that is not yet in self.composition, warn and defer creating a + state_input_port; final check is made, and error(s) generated for unresolved specifications at run time. Assign functions specified in **state_feature_function** to InputPorts for all state_features @@ -2035,17 +2013,17 @@ def _complete_parsing_state_feature_specs(self, context=None): # agent_rep has not yet been (fully) constructed if not agent_rep_input_ports and self.agent_rep_type is COMPOSITION: + # FIX: 3/18/22 - ADD TESTS FOR THESE (in test_deferred_init or test_partial_deferred_init()?) if (isinstance(self.state_feature_specs, set) or isinstance(self.state_feature_specs, dict) and SHADOW_INPUTS not in self.state_feature_specs): - # Dict and set specs reference Nodes of agent_rep, and so must that must be constructed first - raise OptimizationControlMechanismError( - f"The '{STATE_FEATURES}' arg for {self.name} has been assigned a dict or set specification " - f"before any Nodes have been assigned to its {AGENT_REP} ('{self.agent_rep.name}'). Either" - f"those should be assigned before construction of {self.name}, or a list specification " - f"should be used (though that is not advised).") + # Dict and set specs reference Nodes that are not yet in agent_rep + warnings.warn(f"Nodes been specified in the {STATE_FEATURES}' arg for '{self.name}' that are not " + f"(yet) in its its {AGENT_REP} ('{self.agent_rep.name}'). They must all be assigned " + f"to it before the Composition is executed'. It is generally safer to assign all " + f"Nodes to the {AGENT_REP} of a controller before specifying its '{STATE_FEATURES}'.") else: # List and SHADOW_INPUTS specs are dangerous before agent_rep has been fully constructed - warnings.warn(f"The {STATE_FEATURES}' arg for {self.name} has been specified before any Nodes have " + warnings.warn(f"The {STATE_FEATURES}' arg for '{self.name}' has been specified before any Nodes have " f"been assigned to its {AGENT_REP} ('{self.agent_rep.name}'). Their order must be the " f"same as the order of the corresponding INPUT Nodes for '{self.agent_rep.name}' once " f"they are added, or unexpected results may occur. It is safer to assign all Nodes to " @@ -2064,7 +2042,7 @@ def expand_nested_input_comp_to_input_nodes(comp): # PARSE SPECS ------------------------------------------------------------------------------------------ # Generate parallel lists of INPUT Nodes and corresponding feature specs (for sources of inputs) - def _parse_specs(state_feature_specs, spec_str="list"): + def _parse_specs(state_feature_specs, spec_type="list"): """Validate and parse INPUT Node specs assigned to construct state_feature_specs Validate number and identity of specs relative to agent_rep INPUT Nodes. Assign spec for every INPUT Mechanism (nested) within agent_rep (i.e., for all nested Compositions) @@ -2072,13 +2050,16 @@ def _parse_specs(state_feature_specs, spec_str="list"): Return names for use as input_port_names in main body of method """ parsed_feature_specs = [] + num_specs = len(state_feature_specs) + num_ports = len(agent_rep_input_ports) if self.agent_rep_type == COMPOSITION: - # FIX: 3/4/22 - THESE SEEM DUPLICATIVE OF _validate_state_features; JUST CALL THAT HERE? + # FIX: 3/18/22 - THESE SEEM DUPLICATIVE OF _validate_state_features; JUST CALL THAT HERE? # ALSO, WARNING IS TRIGGERED IF MECHANIMS RATHER THAN ITS INPUT_PORTS ARE SPEC'D + # AT THE LEAST, MOVE TO THEIR OWN VALIDATION HELPER METHOD # Too FEW specs for number of agent_rep receivers - if len(self.state_feature_specs) < len(agent_rep_input_ports): + if len(self.state_feature_specs) < num_ports: warnings.warn(f"There are fewer '{STATE_FEATURES}' specified for '{self.name}' than the number " f"of {InputPort.__name__}'s for all of the INPUT Nodes of its {AGENT_REP} " f"('{self.agent_rep.name}'); the remaining inputs will be assigned default values " @@ -2087,27 +2068,28 @@ def _parse_specs(state_feature_specs, spec_str="list"): f"inputs.") # Too MANY specs for number of agent_rep receivers - if len(state_feature_specs) > len(agent_rep_input_ports): + if num_specs > num_ports: # specs_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" # for spec in self._get_specs_not_in_agent_rep(state_feature_specs)] - specs_not_in_agent_rep = [f"'{spec.name if isinstance(spec,(Mechanism, Composition)) else spec.owner.name}'" - for spec in self._get_specs_not_in_agent_rep(user_specs)] + specs_not_in_agent_rep = \ + [f"'{spec.name if isinstance(spec,(Mechanism, Composition)) else spec.owner.name}'" + for spec in self._get_specs_not_in_agent_rep(user_specs)] if specs_not_in_agent_rep: - spec_str = ", ".join(specs_not_in_agent_rep) + spec_type = ", ".join(specs_not_in_agent_rep) warnings.warn( f"The '{STATE_FEATURES}' specified for {self.name} is associated with a number of " f"{InputPort.__name__}s ({len(state_feature_specs)}) that is greater than for the " - f"{InputPort.__name__}s of the INPUT Nodes ({len(agent_rep_input_ports)}) for the " + f"{InputPort.__name__}s of the INPUT Nodes ({num_ports}) for the " f"Composition assigned as its {AGENT_REP} ('{self.agent_rep.name}'), which includes " - f"the following that are not (yet) in '{self.agent_rep.name}': {spec_str}. Executing " + f"the following that are not (yet) in '{self.agent_rep.name}': {spec_type}. Executing " f"{self.name} before the additional item(s) are added as (part of) INPUT Nodes will " f"generate an error.") else: warnings.warn( f"The '{STATE_FEATURES}' specified for {self.name} is associated with a number of " f"{InputPort.__name__}s ({len(state_feature_specs)}) that is greater than for the " - f"{InputPort.__name__}s of the INPUT Nodes ({len(agent_rep_input_ports)}) for the " + f"{InputPort.__name__}s of the INPUT Nodes ({num_ports}) for the " f"Composition assigned as its {AGENT_REP} ('{self.agent_rep.name}'). Executing " f"{self.name} before the additional item(s) are added as (part of) INPUT Nodes will " f"generate an error.") @@ -2118,13 +2100,11 @@ def _parse_specs(state_feature_specs, spec_str="list"): comp_names = ", ".join([f"'{n.name}'" for n in nested_comps]) raise OptimizationControlMechanismError( f"The '{STATE_FEATURES}' argument for '{self.name}' includes one or more Compositions " - f"({comp_names}) in the {spec_str} specified for its '{STATE_FEATURES}' argument; these must be " + f"({comp_names}) in the {spec_type} specified for its '{STATE_FEATURES}' argument; these must be " f"replaced by direct references to the Mechanisms (or their InputPorts) within them to be " f"shadowed.") spec_names = [] - num_specs = len(state_feature_specs) - num_ports = len(agent_rep_input_ports) self._num_state_feature_specs = max(num_specs, num_ports) for i in range(self._num_state_feature_specs): @@ -2157,15 +2137,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): # Pare and assign specs for INPUT Nodes already in agent_rep (i.e., unassigned above) # (others may be added to Composition later) if i < num_specs: - - # Assign either state_feature_specs[i] or self.state_feature_default if specified, else None - if state_feature_specs[i] is not None: - spec = state_feature_specs[i] - elif self.state_feature_default is not None: - spec = self.state_feature_default - else: - spec = None - + spec = state_feature_specs[i] # Assign input_port name if is_numeric(spec): spec_name = f"{port_name} {DEFAULT_VARIABLE.upper()}" @@ -2188,10 +2160,9 @@ def _parse_specs(state_feature_specs, spec_str="list"): elif spec is not None: assert False, f"PROGRAM ERROR: unrecognized form of state_feature specification for {self.name}" - # Fewer specifications than number of INPUT Nodes, - # the remaining ones may be specified later, but for now assume they are meant to be ignored + # Fewer specifications than number of INPUT Nodes, so assign state_feature_default else: - spec = None + spec = self.state_feature_default parsed_feature_specs.append(spec) self._state_feature_functions.append(state_feature_fct) @@ -2203,16 +2174,26 @@ def _parse_specs(state_feature_specs, spec_str="list"): user_specs = self.parameters.state_feature_specs.spec + # SINGLE ITEM spec, SO APPLY TO ALL agent_rep_input_ports + if (user_specs is None + or isinstance(user_specs, (str, tuple, InputPort, OutputPort, Mechanism, Composition)) + or is_numeric(user_specs)): + specs = [user_specs] * len(agent_rep_input_ports) + # OK to assign here (rather than in _parse_secs()) since spec is intended for *all* state_input_ports + self.parameters.state_feature_specs.set(specs, override=True) + input_port_names = _parse_specs(specs, 'list') + # LIST OR SHADOW_INPUTS DICT: source specs # Source specs but not INPUT Nodes specified; spec is either: # - list: [spec, spec...] # - SHADOW_INPUTS dict (with list spec as its only entry): {SHADOW_INPUTS: {[spec, spec...]}} # Treat specs as sources of input to INPUT Nodes of agent_rep (in corresponding order): # Call _parse_specs to construct a regular dict using INPUT Nodes as keys and specs as values - if isinstance(user_specs, list) or (isinstance(user_specs, dict) and SHADOW_INPUTS in user_specs): + elif isinstance(user_specs, list) or (isinstance(user_specs, dict) and SHADOW_INPUTS in user_specs): if isinstance(user_specs, list): - specs = user_specs - spec_str = 'list' + num_missing_specs = len(agent_rep_input_ports) - len(self.state_feature_specs) + specs = user_specs + [self.state_feature_default] * num_missing_specs + spec_type = 'list' else: # SHADOW_INPUTS spec: if isinstance(user_specs[SHADOW_INPUTS], set): @@ -2221,6 +2202,7 @@ def _parse_specs(state_feature_specs, spec_str="list"): f"The '{STATE_FEATURES}' argument for '{self.name}' uses a set in a '{SHADOW_INPUTS.upper()}' " f"dict; this must be a single item or list of specifications in the order of the INPUT Nodes" f"of its '{AGENT_REP}' ({self.agent_rep.name}) to which they correspond." ) + # FIX: 3/18/22 - ?DOES THIS NEED TO BE DONE HERE, OR CAN IT BE DONE IN A RELEVANT _validate METHOD? # All specifications in list specified for SHADOW_INPUTS must be shadowable # (i.e., either an INPUT Node or the InputPort of one) or None; # note: if spec is not in agent_rep, might be added later, @@ -2244,8 +2226,9 @@ def _parse_specs(state_feature_specs, spec_str="list"): f"that are not (part of) any INPUT Nodes of its '{AGENT_REP}' ('{self.agent_rep.name}')." ) specs = user_specs[SHADOW_INPUTS] - spec_str = f"{SHADOW_INPUTS.upper()} dict" - input_port_names = _parse_specs(specs, spec_str=spec_str) + spec_type = f"{SHADOW_INPUTS.upper()} dict" + + input_port_names = _parse_specs(specs, spec_type) # FIX: 2/25/22 - ?ITEMS IN set ARE SHADOWED, BUT UNSPECIFIED ITEMS IN SET AND DICT ARE ASSIGNED DEFAULT VALUES # SET OR DICT: specification by INPUT Nodes @@ -2312,6 +2295,10 @@ def _parse_specs(state_feature_specs, spec_str="list"): input_port_names = _parse_specs(specs) + else: + assert False, f"PROGRAM ERROR: Unanticipated type specified for '{STATE_FEATURES}' arg of '{self.name}: " \ + f"'{user_specs}' ({type(user_specs)})." + # CONSTRUCT InputPort SPECS ----------------------------------------------------------------------------- state_input_port_specs = [] @@ -2404,52 +2391,16 @@ def _parse_state_feature_function(self, feature_function): else: return feature_function - def _update_state_features_dict(self): - agent_rep_input_ports = self._get_agent_rep_input_receivers() - specified_input_ports = self._specified_INPUT_Node_InputPorts_in_order - - for i, port in enumerate(self.state_input_ports): - # Get value (need first, to determine whether it belongs to a nested Comp, for assigning key) - feature = self.state_feature_specs[i] - # Get INPUT Node of agent_rep as key: - if (isinstance(feature, Component) and - feature.owner in [n[0] for n in self.agent_rep._get_nested_nodes()]): - node = feature.owner - elif specified_input_ports[i]: - node = specified_input_ports[i] - elif i < len(agent_rep_input_ports): - node = specified_input_ports[i] = agent_rep_input_ports[i] - else: - node = None - if not (isinstance(node, str) and 'DEFERRED' in node): - continue - if feature.owner not in self._get_agent_rep_input_receivers(comp_as_node=ALL): - # Don't add to dict, will be dealt with or raise an error at run time - continue - self.state_feature_specs[i] = feature - def _update_state_input_ports_for_controller(self, context=None): - """Check and update state_input_ports for model-based optimization (agent_rep==Composition) + """Check and update state_input_ports at run time if agent_rep is a Composition If no agent_rep has been specified or it is a CompositionFunctionApproximator, return (note: validation of state_features specified for CompositionFunctionApproximator optimization is up to the CompositionFunctionApproximator) - For agent_rep that is a Composition): - - - ensure that state_input_ports for all specified state_features are either for InputPorts of INPUT Nodes of - agent_rep, or from Nodes of it or any nested Compositions; - raise an error if any receive a Projection that is not a shadow Projection from an INPUT Node of agent_rep - or from the output_port of a Node that is not somewhere in the agent_rep Composition. - (note: there should already be state_input_ports for any **state_features** specified in the constructor). - - - if no state_features specified, assign a state_input_port for every InputPort of every INPUT Node of agent_rep - (note: shadow Projections for all state_input_ports are created in Composition._update_shadow_projections()). - - - assign state_feature_function to relevant state_input_ports (same function for all if no state_features - are specified or only one state_function is specified; otherwise, use dict for specifications). - - Return True if successful, None if not performed. + For agent_rep that is a Composition, call: + - _update_state_features_dict() + - _validate_state_features() """ # Don't instantiate unless being called by Composition.run() @@ -2472,55 +2423,30 @@ def _update_state_input_ports_for_controller(self, context=None): # BUT MANAGE ERRORS WRT TO _validate_state_features self._update_state_features_dict() self._validate_state_features(context) - return - # FIX: 3/18/22 - ??MOVE THIS TO _complete_parsing_state_feature_specs() - # SHOULD NEVER BE THE CASE HERE?? - elif not self.state_input_ports: - # agent_rep is Composition, but no state_features have been specified, - # so assign a state_input_port to shadow every InputPort of every INPUT node of agent_rep - - # Get list of nodes with any nested Comps that are INPUT Nodes replaced with their respective INPUT Nodes - # (as those are what need to be shadowed) - shadowed_input_ports = [] - for port in self._get_agent_rep_input_receivers(): - if port.internal_only: - continue - # if isinstance(input_port.owner, CompositionInterfaceMechanism): - # input_port = input_port. - shadowed_input_ports.append(port) - - # Instantiate state_input_ports - local_context = Context(source=ContextFlags.METHOD) - state_input_ports = [] - # for input_port in input_ports_not_specified: - for input_port in shadowed_input_ports: - input_port_name = f"{SHADOW_INPUT_NAME}{input_port.owner.name}[{input_port.name}]" - params = {SHADOW_INPUTS: input_port, - INTERNAL_ONLY:True, - PARAMS: {}} - if self.state_feature_function: - # Use **state_feature_function** if specified by user in constructor - params = self._assign_state_feature_function(params) - state_input_port = _instantiate_port(name=input_port_name, - port_type=InputPort, - owner=self, - reference_value=input_port.value, - params=params, - context=local_context) - state_input_ports.append(state_input_port) - - self.add_ports(state_input_ports, - update_variable=False, - context=local_context) - - # Assign OptimizationControlMechanism attributes - self.state_input_ports.data = state_input_ports - self._num_state_feature_specs = len(self.state_input_ports) - self._specified_INPUT_Node_InputPorts_in_order = self._get_agent_rep_input_receivers() - self.parameters.state_feature_specs.set([input_port.shadow_inputs for input_port in self.state_input_ports], - override=True) - return True + def _update_state_features_dict(self): + agent_rep_input_ports = self._get_agent_rep_input_receivers() + specified_input_ports = self._specified_INPUT_Node_InputPorts_in_order + + for i, port in enumerate(self.state_input_ports): + # Get value (need first, to determine whether it belongs to a nested Comp, for assigning key) + feature = self.state_feature_specs[i] + # Get INPUT Node of agent_rep as key: + if (isinstance(feature, Component) and + feature.owner in [n[0] for n in self.agent_rep._get_nested_nodes()]): + node = feature.owner + elif specified_input_ports[i]: + node = specified_input_ports[i] + elif i < len(agent_rep_input_ports): + node = specified_input_ports[i] = agent_rep_input_ports[i] + else: + node = None + if not (isinstance(node, str) and 'DEFERRED' in node): + continue + if feature.owner not in self._get_agent_rep_input_receivers(comp_as_node=ALL): + # Don't add to dict, will be dealt with or raise an error at run time + continue + self.state_feature_specs[i] = feature def _validate_state_features(self, context): """Validate that state_features are legal and consistent with agent_rep. @@ -2542,19 +2468,8 @@ def _validate_state_features(self, context): from psyneulink.core.compositions.composition import \ Composition, CompositionInterfaceMechanism, CompositionError, RunError, NodeRole - # FIX: 3/17/22 - WHY NOT JUST USE: - # self.parameters.state_feature_specs AND - # self._specified_INPUT_Node_InputPorts_in_order[i] - comp = self.agent_rep user_specs = self.parameters.state_feature_specs.spec - # user_specs = self.state_feature_specs - - # FOR DEBUGGING: - self.state_feature_specs - self._specified_INPUT_Node_InputPorts_in_order - assert len(self.state_feature_specs) == len(self._specified_INPUT_Node_InputPorts_in_order) - # ------------- if isinstance(user_specs, dict) and SHADOW_INPUTS in user_specs: state_feature_specs = user_specs[SHADOW_INPUTS] diff --git a/psyneulink/core/components/mechanisms/processing/processingmechanism.py b/psyneulink/core/components/mechanisms/processing/processingmechanism.py index 6eb238cf6a3..8da4cbdfbe5 100644 --- a/psyneulink/core/components/mechanisms/processing/processingmechanism.py +++ b/psyneulink/core/components/mechanisms/processing/processingmechanism.py @@ -93,7 +93,9 @@ from psyneulink.core.components.functions.nonstateful.transferfunctions import SoftMax from psyneulink.core.components.functions.nonstateful.selectionfunctions import OneHot -from psyneulink.core.components.mechanisms.mechanism import Mechanism_Base +from psyneulink.core.components.mechanisms.mechanism import Mechanism_Base, Mechanism +from psyneulink.core.components.ports.inputport import InputPort +from psyneulink.core.components.ports.outputport import OutputPort from psyneulink.core.globals.keywords import \ FUNCTION, MAX_ABS_INDICATOR, MAX_ABS_ONE_HOT, MAX_ABS_VAL, MAX_INDICATOR, MAX_ONE_HOT, MAX_VAL, MEAN, MEDIAN, \ NAME, PROB, PROCESSING_MECHANISM, PREFERENCE_SET_NAME, STANDARD_DEVIATION, VARIANCE @@ -284,7 +286,7 @@ class ProcessingMechanism(ProcessingMechanism_Base): def __init__(self, default_variable=None, size=None, - input_ports:tc.optional(tc.any(list, dict))=None, + input_ports:tc.optional(tc.any(Iterable, Mechanism, OutputPort, InputPort))=None, output_ports:tc.optional(tc.any(str, Iterable))=None, function=None, params=None, diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 92af3edb118..3b2e952e7fe 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -365,7 +365,7 @@ >>> A = ProcessingMechanism(name='Mech') >>> B = ProcessingMechanism(name='Shadowed Mech') - >>> C = ProcessingMechanism(name='Shadowing Mech', input_ports=[B.input_port]) + >>> C = ProcessingMechanism(name='Shadowing Mech', input_ports=B.input_port) >>> ocomp = Composition(pathways=[[A, B], C], ... show_graph_attributes={'direction':'LR'}) >>> ocomp.show_graph(show_node_structure=True) @@ -384,7 +384,7 @@ >>> import psyneulink as pnl >>> mech = pnl.ProcessingMechanism(name='Mech') >>> shadowing_mech = pnl.ProcessingMechanism(name='Shadowing Mech', - ... input_ports=[mech.input_port]) + ... input_ports=mech.input_port) >>> nested_comp = pnl.Composition([mech], name='Nested Composition') >>> outer_comp = pnl.Composition(nodes=[nested_comp, shadowing_mech], ... name='Outer Composition') diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 62893463a6f..a9ff004def1 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -4676,8 +4676,7 @@ def _complete_init_of_partially_initialized_nodes(self, context=None): if self.controller: # Avoid unnecessary updating on repeated calls to run() if self.needs_update_controller and hasattr(self.controller, 'state_input_ports'): - self.needs_update_controller = \ - not self.controller._update_state_input_ports_for_controller(context=context) + self.controller._update_state_input_ports_for_controller(context=context) # Make sure all is in order at run time if context.flags & ContextFlags.PREPARING: @@ -5060,21 +5059,17 @@ def _create_CIM_ports(self, context=None): input_nodes = self.get_nodes_by_role(NodeRole.INPUT) for node in input_nodes: - # loop through all external input ports on input nodes (i.e. ports that are projected to from other nodes) + # loop through all external InputPorts on INPUT Nodes (i.e. Ports that are Projected to from other Nodes) for input_port in node.external_input_ports: - # add it to set of current input ports + # add it to set of current InputPorts current_input_node_input_ports.add(input_port) # if there is not a corresponding CIM InputPort/OutputPort pair, add them if input_port not in set(self.input_CIM_ports.keys()): - # instantiate the input port on the input CIM to correspond to the node's input port + # instantiate the InputPort on the input CIM to correspond to the Node's InputPort interface_input_port = InputPort(owner=self.input_CIM, - # # MODIFIED 2/3/22 OLD: - # variable=input_port.defaults.value, - # MODIFIED 2/3/22 NEW: variable=np.atleast_2d(input_port.defaults.variable)[0], - # MODIFIED 2/3/22 END reference_value=input_port.defaults.value, name= INPUT_CIM_NAME + "_" + node.name + "_" + input_port.name, context=context) @@ -5082,36 +5077,36 @@ def _create_CIM_ports(self, context=None): if NodeRole.TARGET in self.get_roles_by_node(node): interface_input_port.parameters.require_projection_in_composition.set(False, override=True) - # add port to the input CIM + # add Port to the input CIM self.input_CIM.add_ports([interface_input_port], context=context) - # instantiate the output port on the input CIM to correspond to the node's input port + # instantiate the OutputPort on the input CIM to correspond to the Node's InputPort interface_output_port = OutputPort(owner=self.input_CIM, variable=(OWNER_VALUE, functools.partial(self.input_CIM.get_input_port_position, interface_input_port)), function=Identity, name=INPUT_CIM_NAME + "_" + node.name + "_" + input_port.name, context=context) - # add port to the input CIM + # add Port to the input CIM self.input_CIM.add_ports([interface_output_port], context=context) - # add entry to input_CIM_ports dict, so that we can retrieve the CIM ports that correspond to a given - # input node's input port + # add entry to input_CIM_ports dict, so that the CIM ports that correspond to a given + # input node's InputPort can be retrieved self.input_CIM_ports[input_port] = (interface_input_port, interface_output_port) - # create projection from the output port on the input CIM to the input port on the input node + # create Projection from the output port on the input CIM to the input port on the input node projection = MappingProjection(sender=interface_output_port, receiver=input_port, matrix=IDENTITY_MATRIX, name="(" + interface_output_port.name + ") to (" + input_port.owner.name + "-" + input_port.name + ")") - # activate the projection + # activate the Projection projection._activate_for_compositions(self) - # if the node is a nested Composition, activate the projection for the nested Composition as well + # if the node is a nested Composition, activate the Projection for the nested Composition as well if isinstance(node, Composition): projection._activate_for_compositions(node) @@ -5154,7 +5149,7 @@ def _create_CIM_ports(self, context=None): self.output_CIM.add_ports([interface_input_port], context=context) - # instantiate the output port on the output CIM to correspond to the node's output port + # instantiate the OutputPort on the output CIM to correspond to the node's OutputPort interface_output_port = OutputPort( owner=self.output_CIM, variable=(OWNER_VALUE, functools.partial(self.output_CIM.get_input_port_position, @@ -5168,13 +5163,13 @@ def _create_CIM_ports(self, context=None): self.output_CIM.add_ports([interface_output_port], context=context) - # add entry to output_CIM_ports dict, so that we can retrieve the CIM ports that correspond to a given - # output node's output port + # add entry to output_CIM_ports dict, so that CIM ports that correspond to a given + # output node's OutputPort can be retrieved self.output_CIM_ports[output_port] = (interface_input_port, interface_output_port) proj_name = "(" + output_port.name + ") to (" + interface_input_port.name + ")" - # create projection from the output port of the output node to input port on the output CIM + # create Projection from the OutputPort of the output Node to InputPort on the output CIM proj = MappingProjection( sender=output_port, receiver=interface_input_port, @@ -5187,7 +5182,7 @@ def _create_CIM_ports(self, context=None): # activate the projection proj._activate_for_compositions(self) - # if the node is a nested Composition, activate the projection for the nested Composition as well + # if the Node is a nested Composition, activate the Projection for the nested Composition as well if isinstance(node, Composition): proj._activate_for_compositions(node) diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 27f90219c29..6286ce09df7 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -142,7 +142,11 @@ def test_objective_mechanism_spec_as_monitor_for_control_error(self): assert expected_error in error_msg @pytest.mark.parametrize("control_spec", [CONTROL, PROJECTIONS]) - def test_deferred_init(self, control_spec): + @pytest.mark.parametrize("state_features_arg", [ + 'list', + 'dict' + ]) + def test_deferred_init(self, control_spec, state_features_arg): # Test to insure controller works the same regardless of whether it is added to a composition before or after # the nodes it connects to @@ -173,23 +177,44 @@ def test_deferred_init(self, control_spec): comp = pnl.Composition(name="evc", retain_old_simulation_data=True) - # add the controller to the Composition before adding the relevant Mechanisms - comp.add_controller(controller=pnl.OptimizationControlMechanism( - agent_rep=comp, - state_features=[reward.input_port, Input.input_port], - state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), - objective_mechanism=pnl.ObjectiveMechanism( - function=pnl.LinearCombination(operation=pnl.PRODUCT), - monitor=[reward, - Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], - (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), - function=pnl.GridSearch(), - control_signals=[{control_spec: ("drift_rate", Decision), - ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {control_spec: ("threshold", Decision), - ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) - ) + state_features = { + 'list': [reward.input_port, Input.input_port], + 'dict': {reward: reward.input_port, + Input: Input.input_port} + }[state_features_arg] + + if state_features_arg == 'list': + expected_warning = "The state_features' arg for 'OptimizationControlMechanism-0' has been specified " \ + "before any Nodes have been assigned to its agent_rep ('evc'). Their order must " \ + "be the same as the order of the corresponding INPUT Nodes for 'evc' once they are " \ + "added, or unexpected results may occur. It is safer to assign all Nodes to the " \ + "agent_rep of a controller before specifying its 'state_features'." + else: + expected_warning = "that are not in its agent_rep ('evc'). Executing 'evc' before they " \ + "are added will generate an error ." + + with pytest.warns(UserWarning) as warning: + # add the controller to the Composition before adding the relevant Mechanisms + comp.add_controller(controller=pnl.OptimizationControlMechanism( + agent_rep=comp, + state_features = state_features, + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), + objective_mechanism=pnl.ObjectiveMechanism( + function=pnl.LinearCombination(operation=pnl.PRODUCT), + monitor=[reward, + Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], + (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), + function=pnl.GridSearch(), + control_signals=[{control_spec: ("drift_rate", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, + {control_spec: ("threshold", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) + ) + assert any(expected_warning in repr(w.message) for w in warning.list) + assert comp._controller_initialization_status == pnl.ContextFlags.DEFERRED_INIT + assert comp.controller.state_features == {'EXPECTED INPUT NODE 0 OF evc': (reward.input_port), + 'EXPECTED INPUT NODE 1 OF evc': (Input.input_port)} comp.add_node(reward, required_roles=[pnl.NodeRole.OUTPUT]) comp.add_node(Decision, required_roles=[pnl.NodeRole.OUTPUT]) @@ -197,7 +222,8 @@ def test_deferred_init(self, control_spec): comp.add_linear_processing_pathway(task_execution_pathway) comp.enable_controller = True - + assert comp.controller.state_features == {reward.input_port: reward.input_port, + Input.input_port: Input.input_port} # comp._analyze_graph() stim_list_dict = { @@ -855,7 +881,7 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ('set_spec_short', None, None, None), ('set_spec', None, None, None), ('set_spec_port', None, None, None), - ('automatic_assignment', None, None, None), + ('no_specs', None, None, None), ('shadow_inputs_dict_spec', None, None, None), ('shadow_inputs_dict_spec_w_none', None, None, None), ('misplaced_shadow', messages[1], None, pnl.CompositionError), @@ -879,11 +905,7 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c @pytest.mark.control @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x[0] for x in state_feature_args]) - @pytest.mark.parametrize('obj_mech', [ - 'obj_mech', - 'mtr_for_ctl', - None - ]) + @pytest.mark.parametrize('obj_mech', ['obj_mech', 'mtr_for_ctl', None]) def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_args, obj_mech): """See test_nested_composition_as_agent_rep() for additional tests of state_features specification.""" @@ -915,14 +937,14 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'set_spec_short': {oa}, 'set_spec': {ob, icomp, oa}, # Note: out of order is OK, and use of Nested comp as spec 'set_spec_port': {ob.input_port, icomp, oa}, - 'automatic_assignment': None, + 'no_specs': None, 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, # <- ia & ob OK BECAUSE JUST FOR SHADOWING 'shadow_inputs_dict_spec_w_none': {pnl.SHADOW_INPUTS:[ia, None, ob]}, # Illegal state_features specifications - 'misplaced_shadow':ib.input_port, - 'ext_shadow':ext.input_port, - 'ext_output_port':ext.output_port, + 'misplaced_shadow': [ib.input_port], + 'ext_shadow': [ext.input_port], + 'ext_output_port': [ext.output_port], 'input_format_wrong_shape': [ia.input_port, oa.output_port, oc.output_port], 'too_many_inputs_warning': [ia.input_port, oa.output_port, ob.output_port, oc.output_port], 'too_many_inputs_error': [ia.input_port, oa.output_port, ob.output_port, oc.output_port], @@ -936,14 +958,26 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg objective_mechanism = [ic,ib] if obj_mech == 'obj_mech' else None monitor_for_control = [ic] if obj_mech == 'mtr_for_ctl' else None # Needs to be a single item for GridSearch state_features = state_features_dict[test_condition] - ocm = pnl.OptimizationControlMechanism(state_features=state_features, - objective_mechanism=objective_mechanism, - monitor_for_control=monitor_for_control, - function=pnl.GridSearch(), - control_signals=[pnl.ControlSignal(modulates=(pnl.SLOPE,ia), - allocation_samples=[10, 20, 30]), - pnl.ControlSignal(modulates=(pnl.INTERCEPT,oc), - allocation_samples=[10, 20, 30])]) + + if test_condition == 'no_specs': + ocm = pnl.OptimizationControlMechanism(objective_mechanism=objective_mechanism, + monitor_for_control=monitor_for_control, + function=pnl.GridSearch(), + control_signals=[pnl.ControlSignal(modulates=(pnl.SLOPE,ia), + allocation_samples=[10, 20, 30]), + pnl.ControlSignal(modulates=(pnl.INTERCEPT,oc), + allocation_samples=[10, 20, 30])]) + + else: + ocm = pnl.OptimizationControlMechanism(state_features=state_features, + state_feature_default=pnl.SHADOW_INPUTS, + objective_mechanism=objective_mechanism, + monitor_for_control=monitor_for_control, + function=pnl.GridSearch(), + control_signals=[pnl.ControlSignal(modulates=(pnl.SLOPE,ia), + allocation_samples=[10, 20, 30]), + pnl.ControlSignal(modulates=(pnl.INTERCEPT,oc), + allocation_samples=[10, 20, 30])]) if not exception_type: ocomp.add_controller(ocm) @@ -1016,7 +1050,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [0.], [0, 0, 0]])) - elif test_condition == 'automatic_assignment': + elif test_condition == 'no_specs': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', @@ -1064,16 +1098,15 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ocomp.add_controller(ocm) ocomp.run() if test_condition == 'partial_legal_list_spec': - assert len(ocm.state_input_ports) == 1 - assert ocm.state_input_ports.names == ['OA[OutputPort-0]'] + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['OA[OutputPort-0]', + 'Shadowed input of OA[InputPort-0]', + 'Shadowed input of OB[InputPort-0]'] # Note: oa is assigned to icomp due to ordering: assert ocm.state_features == {ia.input_port: oa.output_port, - oa.input_port: None, - ob.input_port: None} + oa.input_port: oa.input_port, + ob.input_port: ob.input_port} assert message_1 in [warning[i].message.args[0] for i in range(len(warning))] - assert ocm.state_features == {ia.input_port: oa.output_port, - oa.input_port: None, - ob.input_port: None} else: with pytest.raises(exception_type) as error: @@ -3244,7 +3277,7 @@ def test_input_CIM_assignment(self, comp_mode): 'nested_full_dict', # <- Specify both of two INPUT Nodes of nested comp in dict format 'nested_comp_set', # <- Specify nested comp as itself in set format 'nested_comp_dict', # <- Specify nested comp as itself in dict format with a single spec for all INPUT Nodes - 'automatic', # <- Automaticaly asign state_features + 'no_spec', # <- Assign default state_features 'bad' # <- Specify Mechanism not in agent_rep ] @pytest.mark.control @@ -3280,7 +3313,7 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ agent_rep = None error_text = '"\'OCM\' has \'state_features\' specified ([\'D[OutputPort-0]\']) that are ' \ 'missing from \'OUTER COMP\' and any Compositions nested within it."' - # state_features = D.output_port if state_features_arg is 'bad' else None + if state_features_arg == 'nested_partial_set': state_features = {A, I2} elif state_features_arg == 'nested_full_set': @@ -3293,21 +3326,26 @@ def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_ state_features = {mcomp} elif state_features_arg == 'nested_comp_dict': state_features = {mcomp:I1} - elif state_features_arg == 'automatic': - state_features = None elif state_features_arg == 'bad': - state_features = D.output_port - else: - assert False, "Bad state_features_arg in test." + state_features = [D.output_port] - ocm = pnl.OptimizationControlMechanism(name='OCM', - agent_rep=agent_rep, - state_features=state_features, - objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), - allow_probes=True, - function=pnl.GridSearch(), - control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I1), - allocation_samples=[10, 20, 30])) + if state_features_arg == 'no_spec': + ocm = pnl.OptimizationControlMechanism(name='OCM', + agent_rep=agent_rep, + objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), + allow_probes=True, + function=pnl.GridSearch(), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I1), + allocation_samples=[10, 20, 30])) + else: + ocm = pnl.OptimizationControlMechanism(name='OCM', + agent_rep=agent_rep, + state_features=state_features, + objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), + allow_probes=True, + function=pnl.GridSearch(), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I1), + allocation_samples=[10, 20, 30])) if state_features_arg == 'bad': with pytest.raises(pnl.OptimizationControlMechanismError) as error: ocomp.add_controller(ocm) From b582287b2a6c79368eb30c5f5d5a540f3f789ec4 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Mon, 21 Mar 2022 12:18:07 -0400 Subject: [PATCH 250/285] Refactor/ocm/state features all as input ports (#2352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • Passes *all* current tests * • composition.py: - add_controller: few more minor mods; still passes all tests * - * - * - * • controlmechanism.py: - __init__: resrict specification to only one of control, modulatory_signals, or control_signals (synonyms) * - * • composition.py: in progress fix of bug in instantiating shadow projections for ocm.state_input_ports * • composition.py: - _get_original_senders(): added support for nested composition needs to be checked for more than one level needs to be refactored to be recursive * • optimizationcontrolmechanism.py - _update_state_input_ports_for_controller: fix invalid_state_features to allow input_CIM of nested comp in agent_rep * - * • composition.py - _get_original_senders: made recursive * • test_show_graph.py: update for fixes * - * • tests: passes all in test_show_graph.py and test_report.py * Passes all tests * - comment clean-up * • composition.py - add_controller and _get_nested_node_CIM_port: added support for forced assignment of NodeRole.OUTPUT for nodes specified in OCM.monitor_for_control, but referenced 'allow_probes' attribute still needs to be implemented * • composition.py, optimizationcontrolmechanism.py: allow_probes fully implemented * • show_graph.py: fixed bug causing extra projections to OCM * • composition.py: - _update_shadow_projections(): fix handling of deep nesting * • optimizationcontrolmechanism.py: add agent_rep_type property * • optimizationcontrolmechanism.py: - state_feature_function -> state_feature_functions * • optimizationcontrolmechanism.py: - _validate_params: validate state_feature_functions - _update_state_input_ports_for_controller: implement assignment of state_feature_functions * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * - * - * - * - * - * - * - * - * • inputport.py: - add figure for shadowing * - * - * • processingmechanism.py: - __init__(): modify typecheck for input_ports to allow single items * • optimizationcontrolmechanism.py - allow single spec (None, array, tuple, or Components) that is assigned to all INPUT Node InputPorts - state_feature_default is assigned to all unspecified INPUT Node InputPorts (for a list that is shorter than the number, a dict or a set that has fewer, or any that are added to agent_rep after controller is initially constructed and added to Composition) * - * - * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * - * - * - * - * - * • optimizationcontrolmechanism.py: - _state_feature_values_getter(): for numeric state_feature, return state_input_port.functio(numeric_value) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - added tests for single state_feature spec * - composition.py, inputport.py, optimizationcontrolmechanism.py: docstring mods re: "external InputPort" * - * - * - * • test_control.py: - test_state_features_in_nested_composition_as_agent_rep(): - add tests for single state_feature specs - add tests for INPUT Node with more than on InputPort * - * - * - * - * - Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- docs/source/Composition.rst | 2 +- .../control/optimizationcontrolmechanism.py | 291 +++++++----- .../processing/objectivemechanism.py | 16 +- psyneulink/core/components/ports/inputport.py | 100 ++-- psyneulink/core/compositions/composition.py | 127 ++--- .../objective/comparatormechanism.py | 6 +- setup.cfg | 1 + tests/composition/test_control.py | 445 ++++++++++++------ 8 files changed, 594 insertions(+), 394 deletions(-) diff --git a/docs/source/Composition.rst b/docs/source/Composition.rst index bbd3c01f0ca..279f431bbb1 100644 --- a/docs/source/Composition.rst +++ b/docs/source/Composition.rst @@ -32,4 +32,4 @@ Composition .. automodule:: psyneulink.core.compositions.composition :members: Composition, NodeRole, Graph :private-members: - :exclude-members: Parameters, show_structure, CompositionError, get_inputs_format + :exclude-members: Parameters, show_structure, CompositionError, get_inputs_format, external_input_ports_of_all_input_nodes, external_input_ports diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index f1f157af73c..dced74d4f18 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -218,15 +218,15 @@ `, are provided as input to it's `evaluate ` method when that is executed to estimate or predict the Composition's `net_outcome `. Those sources of input are used to construct the OptimizationControlMechanism's `state_input_ports - `, one for each external `InputPort` (i.e., that is not designated - as `internal_only `) of each `INPUT ` `Node ` of the - `agent_rep `. The input to each `state_input_port - `, after being processed by it `function `, is - assigned as the corresponding value of `state_feature_values `, - the values of which provided as the input to the corresponding InputPorts of the `INPUT ` `Nodes - of the agent_rep each time it is `evaluated `. Accordingly, the specification requirements - for **state_features** depend on whether the `agent_rep` is a `Composition` - or a `CompositionFunctionApproximator`, as described in each of the two sections below. + `, one for each `external InputPort + ` of the `agent_rep `. The input to + each `state_input_port `, after being processed by it `function + `, is assigned as the corresponding value of `state_feature_values + `, the values of which provided as the input to the corresponding + InputPorts of the `INPUT ` `Nodes ` of the agent_rep each time it is `evaluated + `. Accordingly, the specification requirements for **state_features** depend on whether the + `agent_rep` is a `Composition` or a `CompositionFunctionApproximator`, + as described in each of the two sections below. | @@ -240,9 +240,8 @@ *Automatic assignment.* By default, if **state_features**, **state_feature_default** and **state_feature_function** are not specified, the `state_input_ports ` are configured to - `shadow the inputs ` of every external `InputPort` (i.e., all of the ones not designated - as `internal_only `) of every `INPUT ` `Node ` of the - `agent_rep ` Composition; as a result, each time `agent_rep + `shadow the inputs ` of every `external InputPort ` + of the `agent_rep ` Composition; as a result, each time `agent_rep ` is `evaluated `, it receives the same `external input `) it received during its last `TRIAL` of execution. @@ -288,46 +287,47 @@ * *Single specification* -- any of the indivdiual specifications described `below ` can be directly to **state_features**, that is then used to construct *all* of the `state_input_ports `, one - for each external InputPort (i.e., ones not designated as `internal_only `) of each - `INPUT ` `Node ` of the `agent_rep `. + for each `external InputPort ` of the `agent_rep + `. * *Inputs dictionary* -- specifies state_features (entry values) for individual `InputPorts ` and/or `INPUT ` `Nodes ` of the `agent_rep ` - (entry keys). It must conform to the format used to `specify external inputs ` to the - `agent_rep `, in which entries consist of a key specifying either an `INPUT` - ` `Node ` of the `agent_rep ` or one of - their external `InputPorts `, and a value that is the source of the input that can be any of the forms of - individual input specifications listed `below `. The - format required for the entries can be seen using either the `agent_rep ` + (entry keys). It must conform to the format used to `specify external inputs ` + to the `agent_rep `, in which entries consist of a key specifying either + an `INPUT ` `Node ` of the `agent_rep ` + or one of their `external InputPorts `, and a value that is the source of + the input that can be any of the forms of individual input specifications listed `below + `. The format required for the entries can be seen + using either the `agent_rep ` `get_input_format ` method (for inputs to its `INPUT ` `) or its `external_input_ports_of_all_input_nodes - ` (for all of their external InputPorts). If a nested - Composition is specified (that is, one that is an `INPUT ` Node of `agent_rep - `), the state_feature assigned to it is used to construct the - `state_input_ports ` for *all* of the external InputPorts of all of - the`INPUT ` Nodes for that nested Composition, and any nested within it at all levels of nesting. - If any `INPUT ` Nodes or their InputPorts are not specified in the dictionary, the - `state_feature_default ` is assigned as their state_feature - (this includes cases in which some but not all `INPUT ` Nodes of a nested Composition, or their - InputPorts, are specified; any unspecified INPUT Nodes of the corresponding Compositions are assigned - `state_feature_default ` as their state_feature). + ` (for all of their `external InputPorts + `). If a nested Composition is specified (that is, one that is an `INPUT + ` Node of `agent_rep `), the state_feature assigned to it + is used to construct the `state_input_ports ` for *all* of the + `external InputPorts ` for that nested Composition, and any nested within + it at all levels of nesting. If any `INPUT ` Nodes or their InputPorts are not specified in the + dictionary, `state_feature_default ` is assigned as their + state_feature specification (this includes cases in which some but not all `INPUT ` Nodes of a + nested Composition, or their InputPorts, are specified; any unspecified INPUT Nodes of the corresponding + Compositions are assigned `state_feature_default ` as their + state_feature specification). .. _OptimizationControlMechanism_State_Feature_List_Inputs: - * *List* -- a list of individual state_feature specifications, that can be any of the forms of individual - input specifications listed `below `. - The items correspond to all of the external InputPorts (i.e., ones not designated as `internal_only - `) of all of the `INPUT ` Nodes` of the `agent_rep + * *List* -- a list of individual state_feature specifications, that can be any of the forms of individual input + specifications listed `below `. The items correspond + to all of the `external InputPorts ` of the `agent_rep `, and must be specified in the order they are listed in the `agent_rep `\\'s `external_input_ports_of_all_input_nodes - ` attribute. If the list is incomplete, the remaining - InputPorts are assigned the `state_feature_default `. - Items can be included in the list that have not yet been added to the OptimizationControlMechanism's Composition - or its `agent_rep `. However, these must be added before the Composition - is executed, and must appear in the list in the same position that the InputPorts to which they pertain are listed - in the `agent_rep `\\'s `external_input_ports_of_all_input_nodes - ` attribute, once construction of the `agent_rep - ` is complete. + ` attribute. If the list is incomplete, the remaining + InputPorts are assigned `state_feature_default ` as their + state_feature specification. Items can be included in the list that have not yet been added to the + OptimizationControlMechanism's Composition or its `agent_rep `. However, + these must be added before the Composition is executed, and must appear in the list in the same position that the + InputPorts to which they pertain are listed in the `agent_rep `\\'s + `external_input_ports_of_all_input_nodes ` attribute, + once construction of the `agent_rep ` is complete. .. _OptimizationControlMechanism_State_Feature_Set_Inputs: @@ -337,7 +337,8 @@ during evaluation as when the Composition of which the OptimizationControlMechanism is the `controller ` is fully executed. The order of their specification does not matter; however, any of the `agent_rep `\\'s `INPUT ` Nodes that are *not* included - in the set will be assigned the `state_feature_default `. + in the set are assigned `state_feature_default ` as their + state_feature specification. .. _OptimizationControlMechanism_State_Feature_Individual_Specs: @@ -345,21 +346,25 @@ or in a dict, list or set as described `above `, to configure `state_input_ports `. - .. _OptimizationControlMechanism_Numeric_State_Feature: + .. _OptimizationControlMechanism_None_State_Feature: * *None* -- no `state_input_port ` is constructed for the - corresponding `INPUT ` `Node ` InputPort, and its `default variable - ` is assigned directly to the corresponding value of `state_feature_values - `, and therefore as the input to that InputPort whenever the + corresponding `INPUT ` `Node ` InputPort, and its the value of its `default + variable ` is used as the input to that InputPort whenever the ` is `evaluated `, irrespective of its input when the `agent_rep ` was last executed. - * *numeric value* -- create a `state_input_port ` with the specified - value as its `default variable ` and no `afferent Projections `; - as a result, the specified value is assigned as the input to the corresponding value of `state_feature_values - `, and the coreresponding `INPUT ` `Node - ` InputPort each time the `agent_rep ` is `evaluated - `. + .. _OptimizationControlMechanism_Numeric_State_Feature: + + * *numeric value* -- create a `state_input_port ` has + no `afferent Projections `, and uses the specified value as the input to its + `function `, the result of which is assigned to the corresponding value of + `state_feature_values ` and provided as the input to + the corresponding `INPUT ` `Node ` InputPort each time the `agent_rep + ` is `evaluated `. The specified value must + be compatible with the shape of all of the `external InputPorts ` + of the `agent_rep ` (see `note + ` above). .. _OptimizationControlMechanism_SHADOW_INPUTS_State_Feature: @@ -475,12 +480,12 @@ * **state_feature_function** -- specifies a `function ` to be used as the default function for `state_input_ports `. This is assigned as - the `function ` to any state_input_ports for which no other `Function` is specified; - i.e., in an InputPort specification dictionary ` or `2-item tuple + the `function ` to any state_input_ports for which *no other* `Function` is specified -- + that is, in either an InputPort specification dictionary ` or a `2-item tuple ` in the **state_features** argument (see `state_features `). If either of the latter is specified, they override - the specification in **state_feature_function**. If it is *not* specified, then `LinearCombination` - (the standard default `Function` for an `InputPort`) is assigned to any `state_input_ports + the specification in **state_feature_function**. If **state_feature_function** is *not* specified, then + `LinearCombination` (the standard default `Function` for an `InputPort`) is assigned to any `state_input_ports ` that are not otherwise assigned a `Function`. Specifying functions for `state_input_ports ` can be useful, for example to provide an average or integrated value of prior inputs to the `agent_rep @@ -619,16 +624,20 @@ from the Components specified as the OptimizationControlMechanism's `state_features `, the values of which are assigned as the `state_feature_values `, and conveyed to the `agent_rep -` when it is `executed `. If the -`agent_rep is a Composition `, then the +`\\'s `evaluate ` method when it is `executed +`. The OptimizationControlMechanism has a `state_input_port +` for every specification in the **state_features** arg of its +constructor (see `above `). + +COMMENT: +OLD +If the `agent_rep is a Composition `, then the OptimizationControlMechanism has a state_input_port for every specification in the **state_features** arg of its -contructor (see `above `) or, if none are specified, then a -state_input_port is constructed for every `InputPort` of every `INPUT ` `Node ` -of `agent_rep ` Composition, each of which receives a `Projection` that -`shadows the input ` of those InputPorts. If the `agent_rep is a +constructor (see `above `). If the `agent_rep is a CompositionFunctionApproximator `, then the OptimizationControlMechanism has a state_input_port that receives a Projection from each Component specified in the **state_features** arg of its constructor. +COMMENT COMMENT: In either, case the the `values ` of the @@ -1053,8 +1062,7 @@ def _state_feature_values_getter(owning_component=None, context=None): """Return dict {agent_rep INPUT Node InputPort: value} suitable for **predicted_inputs** arg of evaluate method. Only include entries for sources specified in **state_features**, corresponding to OCM's state_input_ports; - default input will be assigned for all other INPUT Node InputPorts (in composition._instantiate_input_dict()) - + default input will be assigned for all other INPUT Node InputPorts in composition._instantiate_input_dict(). """ # If no state_input_ports return empty list @@ -1084,18 +1092,20 @@ def _state_feature_values_getter(owning_component=None, context=None): state_input_port = owning_component.state_input_ports[i] spec = specified_state_features[i] + # FIX: 3/18/22: REMOVE THIS TO ALLOW INCLUSION OF None SPECS IN DICT: # state_input_port not (yet) specified; default input will be assigned in _instantiate_input_dict() if not isinstance(key, InputPort) or spec is None: continue - - # FIX: 3/4/22 - RESTORE? - # if spec is None: + # # FIX: 3/18/22 - RESTORE? + # elif spec is None: # # # FIX: ??TRY IGNORING RATHER THAN ASSIGNING, AS IT WILL BE ASSIGNED IN _instantiate_input_dict() - # # state_feature_values[state_input_port] = state_input_port.default_input_shape - # continue - - if is_numeric(spec): - state_feature_value = spec + # # MODIFIED 3/18/22 OLD: + # state_feature_value = state_input_port.default_input_shape + # # MODIFIED 3/18/22 NEW: + # # continue + # # MODIFIED 3/18/22 END + elif is_numeric(spec): + state_feature_value = state_input_port.function(spec) elif state_input_port.parameters.value._get(context) is not None: state_feature_value = state_input_port.parameters.value._get(context) else: @@ -1261,28 +1271,15 @@ class OptimizationControlMechanism(ControlMechanism): of Optimization ` for additional details). state_features : Dict[Node:source] - dictionary in which keys are Mechanism's that are `INPUT ` `Nodes ` of - `agent_rep ` and/or any `nested Compositions ` - within it, and values are sources of input specified in **state_features** (or determined automatically). The - latter are provided as the inputs to `state_input_ports `, the - values of which are assigned to `state_feature_values ` and - provided as input to the `agent_rep 's `evaluate ` + dictionary in which keys are all `external InputPorts ` for `agent_rep + `, and values are the sources of their input specified in + **state_features**. These are provided as the inputs to `state_input_ports + `, the `values ` + of which are assigned to `state_feature_values ` and + provided to the `agent_rep `\\'s `evaluate ` method when it is executed (see `state_features ` and `OptimizationControlMechanism_State_Input_Ports` for additional details). - .. technical_note:: - the state_features dict is used by the _build_predicted_inputs() method of an `agent_rep - ` Composition to construct inputs for its `evaluate - ` method. Only Mechanisms are used as keys, to accommodate the possibility - that some but not all of the `INPUT ` `Nodes ` of any nested - composition(s) are specified in the `state_features ` - of the OptimizationControlMechanism's constructor, allowing _build_predicted_inputs() to identify and - provide defaults for any that are not specified. Accordingly, if **state_features** is not specified in - the constructor, and thus assigned automatically, the state_features dictionary will contain entries for - shadowing the InputPorts of all Mechanisms that are `INPUT ` `Nodes ` - in either `agent_rep ` and/or of any Compositions nested at any - level within it. - state_feature_default : Mechanism, InputPort, OutputPort, Projection, dict, SHADOW_INPUTS, numeric value determines the default used if the state_feature (i.e. source) is not otherwise specified for the `InputPort`of an `INPUT ` `Node ` of `agent_rep `. @@ -1297,8 +1294,10 @@ class OptimizationControlMechanism(ControlMechanism): ` method is executed. For each such InputPort, if a `state_feature ` has been specified for it, then its value in state_feature_values is the `value ` of the corresponding `state_input_port - `; otherwise the InputPort's `default_variable - ` is assigned as its value in state_feature_values (see + `. There are no entries for InputPorts for which the + **state_features** specification is ``None`` or it has not been otherwise specified; for those InputPorts, + their `default_variable ` is assigned directly as their input when `agent_rep + ` is `evaluated ` (see `OptimizationControlMechanism_State_Input_Ports` for additional details). state_feature_function : Function of function @@ -2139,6 +2138,9 @@ def _parse_specs(state_feature_specs, spec_type="list"): if i < num_specs: spec = state_feature_specs[i] # Assign input_port name + if isinstance(spec, tuple): + state_feature_fct = spec[1] + spec = spec[0] if is_numeric(spec): spec_name = f"{port_name} {DEFAULT_VARIABLE.upper()}" elif isinstance(spec, (Port, Mechanism, Composition)): @@ -2148,13 +2150,12 @@ def _parse_specs(state_feature_specs, spec_type="list"): spec_name = spec.name elif isinstance(spec, dict): spec_name = spec[NAME] if NAME in spec else f"STATE FEATURE INPUT for {port_name}" - if FUNCTION in spec: - state_feature_fct = spec[FUNCTION] - elif PARAMS in spec and FUNCTION in spec[PARAMS]: - state_feature_fct = spec[PARAMS][FUNCTION] - elif isinstance(spec, tuple): - state_feature_fct = spec[1] - spec = spec[0] + # tuple specification of function (assigned above) overrides dictionary specification + if state_feature_fct is None: + if FUNCTION in spec: + state_feature_fct = spec[FUNCTION] + elif PARAMS in spec and FUNCTION in spec[PARAMS]: + state_feature_fct = spec[PARAMS][FUNCTION] elif spec == SHADOW_INPUTS: spec = port elif spec is not None: @@ -2289,9 +2290,9 @@ def _parse_specs(state_feature_specs, spec_type="list"): else: # Pass values from user_spec dict to be parsed; # corresponding ports are safely in all_specified_ports - # unspecified ports are assigned None per requirements of list format - specs = [expanded_dict_with_ports[port] if port is not None and port in all_specified_ports else None - for port in all_specified_ports] + # unspecified ports are assigned state_feature_default per requirements of list format + specs = [expanded_dict_with_ports[port] if port is not None and port in all_specified_ports + else self.state_feature_default for port in all_specified_ports] input_port_names = _parse_specs(specs) @@ -2309,7 +2310,8 @@ def _parse_specs(state_feature_specs, spec_type="list"): if spec is None: continue spec = _parse_shadow_inputs(self, spec) - # If spec is numeric, assign as default value and InputPort function that simply returns that value + # If spec is numeric, assign its `default_value ` attribute as DEFAULT_VARIABLE, + # and the spec's value to the VALUE entry, which will assign it as its default_variable attribute if is_numeric(spec): spec_val = copy.copy(spec) spec = {VALUE: spec_val, @@ -3242,58 +3244,80 @@ def num_state_input_ports(self): @property def state_features(self): - """Return {InputPort: source} for all INPUT Nodes of agent_rep and sources specified by state_feature_specs. + """Return {InputPort: source} for all INPUT Nodes of agent_rep and/or ones specified in state_feature_specs. + If state_feature_spec is numeric for a Node, assign its value as the source + If existing INPUT Node is not specified in state_feature_specs, assign None as source + If an InputPort is referenced in state_feature_specs that is not yet in agent_rep, + assign "EXPECTED INPUT NODE n" as the entry for the key (where n is the sequential numbering of such refs); + it should be resolved by runtime, or an error is generated. """ # FIX: 3/4/22 - REPLACE "EXPECTED" IN KEY WITH "DEFAULT VALUE FOR " # for unspecified InputPorts if "needs_update_controller" is False # - GET SOURCE OR SHADOWED SPEC self._update_state_features_dict() - agent_rep_input_ports = self._get_agent_rep_input_receivers() - # FIX: CONSTRAIN TO ONLY GET SOURCES: HERE OR BY ADDING ARG TO state_distal_sources_and_destinations_dict() - sources = [source_tuple[0] if source_tuple[0] != DEFAULT_VARIABLE else value - for source_tuple, value in self.state_feature_sources.items()] + agent_rep_input_ports = self.agent_rep.external_input_ports_of_all_input_nodes + sources = [np.array(s).tolist() if is_numeric(s) else s + for s in list(self._get_state_feature_sources().values())] # FIX: USES SOURCES AS VALUES FOR DICT BELOW state_features_dict = {} # Use num_state_feature_specs here instead of num_state_input_ports as there may be some "null" (None) specs j = 0 for i in range(self._num_state_feature_specs): + spec = self.state_feature_specs[i] + # FIX: 3/20/22 - USE KEYS RETURNED FROM _get_state_feature_sources?? # Assign InputPorts of INPUT Nodes of agent_rep as keys if self._specified_INPUT_Node_InputPorts_in_order[i] in agent_rep_input_ports: key = self._specified_INPUT_Node_InputPorts_in_order[i] else: key = f"EXPECTED INPUT NODE {i} OF {self.agent_rep.name}" - if self.state_feature_specs[i] is not None: + if spec is not None: state_features_dict[key] = sources[j] j += 1 else: - state_features_dict[key] = None + state_features_dict[key] = spec return state_features_dict - @property - def state_feature_sources(self): + # FIX: 3/20/22 - RESTORE THIS AS PROPERTY ONCE source_and_destinations IS EITHER REMOVED OR REFACTORED SIMILARLY + def _get_state_feature_sources(self): """Dict with {InputPort: source} for all INPUT Nodes of agent_rep, and sources in **state_feature_specs.""" - state_dict = {} - # FIX: 3/4/22 - THIS NEEDS TO HANDLE BOTH state_input_ports BUT ALSO state_feature_values FOR WHICH THERE ARE NO INPUTPORTS + source_dict = {} specified_state_features = [spec for spec in self.state_feature_specs if spec is not None] + missing_port_index = 0 for state_index, port in enumerate(self.state_input_ports): if not port.path_afferents: if port.default_input is DEFAULT_VARIABLE: - # MODIFIED 3/4/22 OLD: - source_port = DEFAULT_VARIABLE - # # MODIFIED 3/4/22 NEW: - # if self.state_feature_specs[state_index] is not None: - # source_port = self.state_feature_specs[state_index] - # else: - # source_port = DEFAULT_VARIABLE - # MODIFIED 3/4/22 END - node = None - comp = None + if specified_state_features[state_index] is not None: + source = specified_state_features[state_index] + else: + source = DEFAULT_VARIABLE else: - source_port = specified_state_features[state_index] - node = None - comp = None + source = specified_state_features[state_index] + input_node = f"EXPECTED INPUT NODE {missing_port_index} OF {self.agent_rep.name}" + missing_port_index += 1 else: + get_info_method = self.composition._get_source + # FIX: 1/8/22: ASSUMES ONLY ONE PROJECTION PER STATE FEATURE + if port.shadow_inputs: + port = port.shadow_inputs + if port.owner in self.composition.nodes: + composition = self.composition + else: + composition = port.path_afferents[0].sender.owner.composition + get_info_method = composition._get_destination + source, _, comp = get_info_method(port.path_afferents[0]) + input_node = self._specified_INPUT_Node_InputPorts_in_order[state_index] + source_dict.update({input_node: source}) + return source_dict + + @property + def state_feature_sources(self): + """Dict with {InputPort: source} for all INPUT Nodes of agent_rep, and sources in **state_feature_specs.""" + state_dict = {} + # FIX: 3/4/22 - THIS NEEDS TO HANDLE BOTH state_input_ports BUT ALSO state_feature_values FOR WHICH THERE ARE NO INPUTPORTS + specified_state_features = [spec for spec in self.state_feature_specs if spec is not None] + for state_index, port in enumerate(self.state_input_ports): + if port.path_afferents: get_info_method = self.composition._get_source # MODIFIED 1/8/22: ONLY ONE PROJECTION PER STATE FEATURE if port.shadow_inputs: @@ -3304,6 +3328,15 @@ def state_feature_sources(self): composition = port.path_afferents[0].sender.owner.composition get_info_method = composition._get_destination source_port, node, comp = get_info_method(port.path_afferents[0]) + else: + if port.default_input is DEFAULT_VARIABLE: + source_port = DEFAULT_VARIABLE + node = None + comp = None + else: + source_port = specified_state_features[state_index] + node = None + comp = None state_dict.update({(source_port, node, comp, state_index):self.state[state_index]}) return state_dict diff --git a/psyneulink/core/components/mechanisms/processing/objectivemechanism.py b/psyneulink/core/components/mechanisms/processing/objectivemechanism.py index c81cdf8adc7..84b69156e63 100644 --- a/psyneulink/core/components/mechanisms/processing/objectivemechanism.py +++ b/psyneulink/core/components/mechanisms/processing/objectivemechanism.py @@ -180,10 +180,10 @@ ` and/or 'exponent ` attributes of the corresponding InputPorts, it can be configured to calculate differences, ratios, etc. (see `example ` below). The `function ` can also -be replaced with any `CombinationFunction`, or any python function that takes an 2d array as its input (with a number -of items in axis 0 equal to the number of the ObjectiveMechanism's InputPorts), and generates a 1d array as its result. -If it implements :keyword:`weight` and/or :keyword:`exponent` attributes, those are assigned from `weight -` and `exponent ` attributes of its `input_ports +be replaced with any `CombinationFunction `, or any python function that takes an 2d array as +its input (with a number of items in axis 0 equal to the number of the ObjectiveMechanism's InputPorts), and generates +a 1d array as its result. If it implements :keyword:`weight` and/or :keyword:`exponent` attributes, those are assigned +from `weight ` and `exponent ` attributes of its `input_ports ` (also listed in the `monitor_weights_and_exponents ` attribute); otherwise, they are ignored. @@ -464,10 +464,10 @@ class ObjectiveMechanism(ProcessingMechanism_Base): OutputPorts specified in its `monitor ` attribute. function : CombinationFunction, ObjectiveFunction, function, or method - the function used to evaluate the values monitored by the ObjectiveMechanism. The function can be - any PsyNeuLink `CombinationFunction` or a Python function that takes a 2d array with an arbitrary number of - items or a number equal to the number of items in the ObjectiveMechanism's variable (i.e., its number of - input_ports) and returns a 1d array. + the function used to evaluate the values monitored by the ObjectiveMechanism. The function can be any + `CombinationFunction ` or a Python function that takes a 2d array with an arbitrary + number of items or a number equal to the number of items in the ObjectiveMechanism's variable (i.e., + its number of input_ports) and returns a 1d array. output_port : OutputPort contains the `primary OutputPort ` of the ObjectiveMechanism; the default is diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index 3b2e952e7fe..ccd7a618246 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -39,10 +39,10 @@ provided by the `Projections ` to that Mechanism from others in a `Composition`. If the InputPort belongs to an `ORIGIN` Mechanism (see `Mechanism_Role_In_Compositions`), then it receives the input specified when that Composition is `run `. The `PathwayProjections ` received by an InputPort are listed in its -`path_afferents `, and its `ModulatoryProjections ` in its `mod_afferents -` attribute. Its `function ` combines the values received from its -PathWayProjections, modifies the combined value according to value(s) any ModulatoryProjections it receives, and -provides the result to the assigned item of its owner Mechanism's `variable ` and +`path_afferents `, and its `ModulatoryProjections ` in its +`mod_afferents ` attribute. Its `function ` combines the values received +from its PathWayProjections, modifies the combined value according to value(s) any ModulatoryProjections it receives, +and provides the result to the assigned item of its owner Mechanism's `variable ` and `input_values ` attributes (see `below` and `Mechanism InputPorts ` for additional details about the role of InputPorts in Mechanisms, and their assignment to the items of a Mechanism's `variable ` attribute). @@ -80,7 +80,7 @@ An InputPort must be owned by a `Mechanism `. When InputPort is specified in the constructor for a Mechanism (see `below `), it is automatically assigned to that Mechanism as its owner. If -the InputPort is created on its own, its `owner ` can specified in the **owner** argument of its +the InputPort is created on its own, its `owner ` can specified in the **owner** argument of its constructor, in which case it is assigned to that Mechanism. If its **owner** argument is not specified, its initialization is `deferred ` until COMMENT: @@ -145,7 +145,6 @@ ` and `Mechanism InputPort specification ` for details). - Adding InputPorts to a Mechanism after it is created ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -253,8 +252,8 @@ An InputPort can also be specified by specifying one or more Ports, Mechanisms or Projections that should project to it, as described below. Specifying an InputPort in this way creates both the InputPort and any of the specified or implied Projection(s) to it (if they don't already exist). `MappingProjections ` - are assigned to the InputPort's `path_afferents ` attribute, while `ControlProjections - ` and `GatingProjections ` to its `mod_afferents ` + are assigned to the InputPort's `path_afferents ` attribute, while `ControlProjections + ` and `GatingProjections ` to its `mod_afferents ` attribute. Any of the following can be used to specify an InputPort by the Components that projection to it (see `below ` for an explanation of the relationship between the `value` of these Components and the InputPort's `variable `): @@ -292,12 +291,12 @@ * **value, Port specification, or list of Port specifications** -- specifies either the `variable ` of the InputPort, or one or more Ports that should project to it. The Port specification(s) can be a (Port name, Mechanism) tuple (see above), and/or include Mechanisms (in which - case their `primary OutputPort ` is used. All of the Port specifications must be + case their `primary OutputPort ` is used. All of the Port specifications must be consistent with (that is, their `value ` must be compatible with the `variable ` of) the Projection specified in the fourth item if that is included; * **weight** -- must be an integer or a float; multiplies the `value ` of the InputPort - before it is combined with others by the Mechanism's `function ` (see + before it is combined with others by the Mechanism's `function ` (see ObjectiveMechanism for `examples `); * **exponent** -- must be an integer or float; exponentiates the `value ` of the @@ -348,9 +347,9 @@ one of two ways: 1) by assigning it `INPUT ` as a `required NodeRole ` where it is added to the nested Composition; and/or 2) by doing the following: a) add a Mechanism to the nested Composition with an InputPort that shadows the one to be - shadowed; b) specify `OUTPUT ` as a `required_role ` - for that Mechanism; c) use that Mechanism as the `InputPort specification ` - for the shadowing InputPort. + shadowed; b) specify `INPUT ` in the **required_roles** argument of the Composition's + `Composition.add_node` method used to add that Mechanism to the Composition; c) use that Mechanism as the + `InputPort specification ` for the shadowing InputPort. .. _InputPort_Shadow_Inputs_Figures: @@ -397,10 +396,10 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The `variable ` of an InputPort must be compatible with the item of its owner Mechanism's -`variable ` to which it is assigned (see `Mechanism_Variable_and_InputPorts`). This may -have consequences that must be taken into account when `specifying an InputPort by Components that project to it -`. These depend on the context in which the specification is made, and -possibly the value of other specifications. These considerations and how they are handled are described below, +`variable ` to which it is assigned (see `Mechanism `). +This may have consequences that must be taken into account when `specifying an InputPort by Components that project +to it `. These depend on the context in which the specification is made, +and possibly the value of other specifications. These considerations and how they are handled are described below, starting with constraints that are given the highest precedence: * **InputPort is** `specified in a Mechanism's constructor ` and the @@ -476,9 +475,9 @@ --------- Every InputPort is owned by a `Mechanism `. It can receive one or more `MappingProjections -` from other Mechanisms, as well as from the Process or System to which its owner belongs (if it -is the `ORIGIN` Mechanism for that Process or System). It has the following attributes, that includes ones specific -to, and that can be used to customize the InputPort: +` from other Mechanisms or itself (e.g., `RecurrentTransferMechanism`), as well as from the +Composition to which its owner belongs (if it is the `ORIGIN` Mechanism for that Process or System). It has the +following attributes, that includes ones specific to, and that can be used to customize the InputPort: * `projections ` -- all of the `Projections ` received by the InputPort. @@ -486,15 +485,25 @@ * `path_afferents ` -- `MappingProjections ` that project to the InputPort, the `value `\\s of which are combined by the InputPort's `function `, - possibly modified by its `mod_afferents `, and assigned to the corresponding item of the - owner Mechanism's `variable `. + possibly `modulated ` by one or more `mod_afferent ` + `GatingProjections `, and assigned to the corresponding item of the owner Mechanism's + `variable `. -* `mod_afferents ` -- `GatingProjections ` that project to the +* `mod_afferents ` -- `GatingProjections ` that project to the InputPort, the `value ` of which can modify the InputPort's `value ` (see the descriptions of Modulation under `ModulatorySignals ` and `GatingSignals ` for additional details). If the InputPort receives more than one GatingProjection, their values are combined before they are used to modify the `value ` of InputPort. + .. _InputPort_Internal_Only: + +* `internal_only ` -- specifies whether an InputPort that belongs to an `INPUT + ` `Node ` of a `Composition` receives `Projections ` *only from* + other `Nodes ` *within* the Composition to which its `owner ` belongs; such + InputPorts do not receive any `external inputs ` to that Composition when it is + `executed ` (see `Composition inputs ` + for additional information). + .. _InputPort_Variable: * `variable ` -- serves as the template for the `value ` of the @@ -504,8 +513,8 @@ ` to which the InputPort is assigned (see `above ` and `Mechanism InputPort specification `). The InputPort's `variable ` is composed of the concatenated `values ` of its `path_afferent - ` Projections. If it has none, then the `variable ` is either assigned - None or its `default value `, as determined by its `default_input ` + ` Projections. If it has none, then the `variable ` is either assigned + None or its `default value `, as determined by its `default_input ` setting. .. _InputPort_Function: @@ -513,9 +522,9 @@ * `function ` -- combines the `value ` of all of the `Projections ` received by the InputPort, and assigns the result to the InputPort's `value ` attribute. The default function is `LinearCombination` that performs an elementwise (Hadamard) - sums the values. However, the parameters of the `function ` -- and thus the `value + sums the values. However, the parameters of the `function ` -- and thus the `value ` of the InputPort -- can be modified by any `GatingProjections ` received by - the InputPort (listed in its `mod_afferents ` attribute. A custom function can also be + the InputPort (listed in its `mod_afferents ` attribute. A custom function can also be specified, so long as it generates a result that is compatible with the item of the Mechanism's `variable ` to which the `InputPort is assigned `. @@ -685,7 +694,7 @@ class InputPort(Port_Base): specifies the value of the `exponent ` attribute of the InputPort. internal_only : bool : False - specifies whether the InputPort requires external input when its `owner ` is the `INPUT` + specifies whether the InputPort requires external input when its `owner ` is the `INPUT` `Node ` of a `Composition (see `internal_only ` for details). Attributes @@ -700,7 +709,7 @@ class InputPort(Port_Base): default_input : None or DEFAULT_VARIABLE determines the value used as `variable ` if the InputPort has no `path_afferent - ` `Projections `. If None (the default), then None is used. This is the + ` `Projections `. If None (the default), then None is used. This is the default behavior, as it is useful for identifying "orphaned" InputPorts (i.e., ones that do not receive any inputs) and the `Mechanisms ` to which they belong, as an error is returned if an InputPort is executed and its variable is assigned None. If *default_input* is assigned *DEFAULT_VARIABLE*, then the @@ -710,7 +719,7 @@ class InputPort(Port_Base): .. note:: If `default_input ` is assigned *DEFAULT_VARIABLE*, then its `internal_only - ` attribute is automtically assigned True. This is so that if the `Mechanism` + ` attribute is automatically assigned True. This is so that if the `Mechanism` to which the InputPort belongs is assigned to a `Composition`, it is not treated as an `ORIGIN ` `Node ` of that Composition (and automatically assigned a Projection from its `input_CIM `. @@ -720,20 +729,21 @@ class InputPort(Port_Base): expected for any `path_afferent Projections `. function : Function - if it is a `CombinationFunction`, it combines the `values ` of the `PathwayProjections - ` (e.g., `MappingProjections `) received by the InputPort (listed in - its `path_afferents ` attribute), under the possible influence of `GatingProjections - ` received by the InputPort (listed in its `mod_afferents ` - attribute). The result is assigned to the InputPort's `value ` attribute. For example, the - the default (`LinearCombination` with *SUM* as it **operation**) performs an element-wise (Hadamard) sum of its - Projection `values `, and assigns to `value ` an array that is of the - same length as each of the Projection `values `. If the InputPort receives only one - Projection, then any other function can be applied and it will generate a value that is the same length as the - Projection's `value `. However, if the InputPort receives more than one Projection and + if it is a `CombinationFunction `, it combines the `values ` of + the `PathwayProjections ` (e.g., `MappingProjections `) received by the + InputPort (listed in its `path_afferents ` attribute), under the possible + influence of `GatingProjections ` received by the InputPort (listed in its `mod_afferents + ` attribute). The result is assigned to the InputPort's `value ` + attribute. For example, the the default (`LinearCombination` with *SUM* as it **operation**) performs an + element-wise (Hadamard) sum of its Projection `values `, and assigns to `value + ` an array that is of the same length as each of the Projection `values + `. If the InputPort receives only one Projection, then any other function can be + applied and it will generate a value that is the same length as the Projection's `value + `. However, if the InputPort receives more than one Projection and uses a function other than a CombinationFunction, a warning is generated and only the `value - ` of the first Projection list in `path_afferents ` is used - by the function, which may generate unexpected results when executing the Mechanism or Composition to which it - belongs. + ` of the first Projection list in `path_afferents ` + is used by the function, which may generate unexpected results when executing the Mechanism or Composition + to which it belongs. value : value or ndarray the output of the InputPort's `function `, that is assigned to an item of the owner @@ -893,7 +903,7 @@ def __init__(self, if default_input == DEFAULT_VARIABLE: internal_only = True - # If owner or reference_value has not been assigned, defer init to Port._instantiate_projection() + # If owner or reference_value has not been assigned, defer init to Port_Base._instantiate_projection() # if owner is None or (variable is None and reference_value is None and projections is None): if owner is None: # Temporarily name InputPort @@ -1004,7 +1014,7 @@ def _validate_params(self, request_set, target_set=None, context=None): f"({ target_set[EXPONENT]}) must be an int or float.") def _validate_against_reference_value(self, reference_value): - """Validate that Port.value is compatible with reference_value + """Validate that Port_Base.value is compatible with reference_value reference_value is the item of the owner Mechanism's variable to which the InputPort is assigned """ diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index a9ff004def1..2b44976f831 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -380,8 +380,8 @@ *Inputs for nested Compositions*. If a nested Composition is an `INPUT` Node of all of the Compositions within which it is nested, including the outermost one, then when the latter is `executed `, the `inputs specified ` to its `execution method ` must -include the InputPorts of the nested Composition. These can be accessed using the Composition's `exernal_input_ports -` attribute. +include the InputPorts of the nested Composition. These can be accessed using the Composition's +`external_input_ports_of_all_input_nodes ` attribute. .. _Composition_Nested_Results: @@ -838,7 +838,7 @@ It also assigns the following item to the list of `learning_components` for the pathway: .. _OUTPUT_MECHANISM: - * *OUTPUT_MECHANISM* -- the final `Node ` in the learning Pathway, the target `value + * *OUTPUT_MECHANISM* -- the final `Node ` in the learning Pathway, the target `value ` for which is specified as input to the `TARGET_MECHANISM`; the Node is assigned the `NodeRoles ` `OUTPUT` in the Composition. @@ -1005,6 +1005,8 @@ - `Execution Methods ` - `Composition_Execution_Inputs` + • `Composition_Input_Dictionary` + • `Composition_Programmatic_Inputs` - `Composition_Execution_Factors` • `Composition_Runtime_Params` • `Composition_Cycles_and_Feedback` @@ -1110,13 +1112,16 @@ format can be used for the `execute ` method, since it executes only one `TRIAL ` at a time, and therefore can only accept inputs for asingle `TRIAL `. +.. _Composition_Input_External_InputPorts: + *Inputs and input_ports*. All formats must specify the inputs to be assigned, on each `TRIAL `, to -the InputPorts of the Composition's `INPUT` `Nodes ` that require external inputs. These are listed -in the `external_input_ports ` attribute of the Composition's `INPUT` -`Mechanisms `, and the corresponding attribute (`external_input_ports `) -of any `nested Composition ` that is an `INPUT` Node of the Composition being executed -(see `above `). The format required can also be seen using the -`get_input_format() ` method. +*all* of the **external InputPorts** of the Composition's `INPUT` `Nodes `. These are InputPorts +belonging to its `INPUT` `Nodes ` at *all levels of nesting*, that are not designated as +`internal_only `. They are listed in the Composition's `external_input_ports_of_all_input_nodes +` attribute, as well as the `external_input_ports +` attribute of each `Mechanism` that is an `INPUT ` +`Node ` of the Composition or any `nested Composition ` within it +The format required can also be seen using the `get_input_format() ` method. .. _Composition_Input_Internal_Only: @@ -1127,10 +1132,10 @@ `). Conversely, some Mechanisms have InputPorts that are designated as `internal_only ` (for example, the `input_port ` for a `RecurrentTransferMechanism`, if its `has_recurrent_input_port ` - attribute is True), in which case no input should be specified for those input_ports. Similar considerations - extend to the `external_input_ports ` of a `nested Composition - `, based on the Mechanisms (and/or additionally nested Compositions) that comprise its set of - `INPUT` `Nodes `. + attribute is True), in which case no input should be specified for those input_ports. Similar considerations + extend to the `external_input_ports_of_all_input_nodes ` of a + `nested Composition `, based on the Mechanisms (and/or additionally nested Compositions) that + comprise its set of `INPUT` `Nodes `. The factors above determine the format of each entry in an `inputs dictionary `, or the return value of the function or generator used for `programmatic specification ` of @@ -1159,6 +1164,8 @@ information concerning `entries for Nodes ` and `entries for InputPorts `). +.. _Composition_Input_Dictionary_Input_Values: + *Input values*. The value of each entry is an ndarray or nested list containing the inputs to that Node or InputPort. For Nodes, the value is a 3d array (or correspondingly nested list), in which the outermost items are 2d arrays containing the 1d array of input values to each of the Node's InputPorts for a given `TRIAL `. For @@ -1175,19 +1182,19 @@ .. _Composition_Input_Dictionary_Node_Entries: -*Node entries*. The key must be an `INPUT ` `Node `of the Composition, or the name of +*Node entries*. The key must be an `INPUT ` `Node ` of the Composition, or the name of one (i.e., the str in its `name ` attribute), and the value must specify the input to *all* of its -InputPorts (other than those designated as `internal_only `; see `above -`) for one or all `TRIAL `\\s of execution. The values for each -`TRIAL ` must be compatible with each of the corresponding InputPorts (listed in the +InputPorts (other than those designated as `internal_only `; see `note +` above) for one or all `TRIAL `\\s of execution. The values +for each `TRIAL ` must be compatible with each of the corresponding InputPorts (listed in the `external_input_ports ` attribute of a Mechanism, and similarly in the -`external_input_ports ` attribute of a Composition). More specifically, -the shape of each item in the outer dimension (i.e., the input for each `TRIAL `, as described `above -`) must be compatible with the shape of the Node's -`external_input_shape ` attribute if it is Mechanism, and similarly the -`external_input_shape ` attribute of a Composition). While these are always 2d -arrays, the number and size of the 1d arrays within them (corresponding to each InputPort) may vary; in some case -shorthand notations are allowed, as illustrated in the `examples ` below. +`external_input_ports_of_all_input_nodes ` attribute of a +Composition). More specifically, the shape of each item in the outer dimension (i.e., the input for each `TRIAL +`, as described `above `) must be compatible with the +shape of the Node's `external_input_shape ` attribute if it is Mechanism, and +similarly the `external_input_shape ` attribute of a Composition). While these are +always 2d arrays, the number and size of the 1d arrays within them (corresponding to each InputPort) may vary; in some +case shorthand notations are allowed, as illustrated in the `examples ` below. .. _Composition_Execution_Input_Dict_Fig: @@ -1205,21 +1212,20 @@ .. _Composition_Input_Dictionary_InputPort_Entries: -*InputPort Entries*. The key must be an external `InputPort` (that is, one that is not designated as `internal_only -`; see `above `) for an `INPUT ` `Node -` of the Composition, or the `full_name ` of one, and the value must specify the -input for one or all `TRIAL `\\s of execution. Any or all of the InputPorts for an`INPUT ` -`Node ` can be specified, but an inputs dictionary cannot have specifications for both the Node -and any of its InputPorts. If the name of an InputPort is used as the key, its the str in its `full_name -` attribute must be used, to ensure disambiguation from any similarly named InputPorts of other -Nodes. Specifying InputPorts individually (instead of specifying all of them in a single entry for a Node) can be -if only some InputPorts should receive inputs, or the input for some needs to remain constant across `TRIAL -`\\s (by providing it with only one input value) while the input to others vary (i.e., by providing -input_values for every `TRIAL `). The value of each entry must be a 2d array or nested list -containing the input for either a single `TRIAL ` or all `TRIAL `\\s, each of which -must match the `input_shape ` of the InputPort. As with Nodes, if there are entries for some -but not all of a Node's InputPorts, the ones not specified are assigned their `default_input ` -values for every `TRIAL ` of execution. +*InputPort Entries*. The key must be an `external InputPort ` for an +`INPUT ` `Node ` of the Composition, or the `full_name ` of one, +and the value must specify the input for one or all `TRIAL `\\s of execution. Any or all of the +InputPorts for an`INPUT ` `Node ` can be specified, but an inputs dictionary cannot +have specifications for both the Node and any of its InputPorts. If the name of an InputPort is used as the key, its +the str in its `full_name ` attribute must be used, to ensure disambiguation from any similarly +named InputPorts of other Nodes. Specifying InputPorts individually (instead of specifying all of them in a single +entry for a Node) can be if only some InputPorts should receive inputs, or the input for some needs to remain constant +across `TRIAL `\\s (by providing it with only one input value) while the input to others vary +(i.e., by providing input_values for every `TRIAL `). The value of each entry must be a 2d array +or nested list containing the input for either a single `TRIAL ` or all `TRIAL `\\s, +each of which must match the `input_shape ` of the InputPort. As with Nodes, if there are +entries for some but not all of a Node's InputPorts, the ones not specified are assigned their `default_input +` values for every `TRIAL ` of execution. COMMENT: Example of input dictionary show various ways of specifying inputs for Nodes and InputPorts:: @@ -1252,7 +1258,7 @@ ... [[14]],[[15]]]} # for each TRIAL of execution >>> outer_comp.get_input_format() >>> outer_comp.external_input_shape - >>> outer_comp.get_external_input_ports + >>> outer_comp.external_input_ports_of_all_input_nodes >>> outer_comp.run(inputs=inputs) Add output here @@ -1485,10 +1491,10 @@ def input_function(env, result): Runtime parameter values for a Composition are specified in a dictionary assigned to the **runtime_params** argument of a Composition's `execution method `. The key of each entry is a Node of the Composition, and the value is a subdictionary specifying the **runtime_params** argument that will be passed to the -Node when it is executed. The format of the dictionary for each `Node ` follows that for a Mechanism's -`runtime specification dictionary `, except that in addition to specifying the -value of a parameter directly (in which case, the value will apply throughout the execution), its value can also be -placed in a tuple together with a `Condition` specifying when that value should be applied, as follows: +Node when it is executed. The format of the dictionary for each `Node ` follows that for a +Mechanism's `runtime specification dictionary `, except that in addition to +specifying the value of a parameter directly (in which case, the value will apply throughout the execution), its value +can also be placed in a tuple together with a `Condition` specifying when that value should be applied, as follows: * Dictionary assigned to **runtime_parms** argument: {: Runtime Parameter Specification Dictionary} - *key* - Node @@ -1774,10 +1780,10 @@ def input_function(env, result): COMMENT: ?? OR JUST THEIR `previous_value ` ?? COMMENT - of its `StatefulFunction`. If it is called without any arguments, it calls the `reset ` method - for every `Node ` in the Composition that has a `StatefulFunction`. It can also be called with a - dictionary that specifies a subsset of Nodes to reset (see format descdribed for **reset_stateful_functions_when** - below). + of its `StatefulFunction`. If it is called without any arguments, it calls the `reset ` + method for every `Node ` in the Composition that has a `StatefulFunction`. + It can also be called with a dictionary that specifies a subsset of Nodes to reset (see format descdribed for + **reset_stateful_functions_when** below). * **reset_stateful_functions_when** and **reset_stateful_functions_to** -- these are arguments of the Composition's `run ` and `learn ` methods, that can be used to specify the `Conditions @@ -2154,9 +2160,10 @@ def input_function(env, result): `. However, some Mechanisms may have InputPorts marked as `internal_only ` which are excluded from its `external_input_ports ` and therefore its `external_input_variables `, and so should not receive - an input value. The same considerations extend to the `external_input_ports ` - and `external_input_variables ` of a Composition, based on the Mechanisms - and/or `nested Compositions ` that comprise its `INPUT` Nodes. + an input value. The same considerations extend to the `external_input_ports_of_all_input_nodes + ` and `external_input_variables + ` of a Composition, based on the Mechanisms and/or `nested Compositions + ` that comprise its `INPUT` Nodes. If num_trials is not in use, the number of inputs provided determines the number of `TRIAL `\\s in the run. For example, if five inputs are provided for each `INPUT` `Node `, and num_trials is not @@ -2902,7 +2909,7 @@ class Graph(object): maps `Component` in the graph to the `Vertices ` that represent them. vertices : List[Vertex] - the `Vertices ` contained in this Graph; each can be a `Node ` or a + the `Vertices ` contained in this Graph; each can be a `Node ` or a `Projection `. dependency_dict : Dict[`Component` : Set(`Component`)] @@ -3502,19 +3509,19 @@ class Composition(Composition_Base, metaclass=ComponentsMeta): `, then from any `Nodes ` in the outer composition that project to the nested Composition (either itself, as a Node in the outer Composition, or to any of its own Nodes). - external_input_ports_of_all_input_nodes: list[InputPort] - a list of all external InputPorts (i.e., ones not desginated `internal_only `) of - all `INPUT ` `Nodes ` of the Composition, including any that in `nested - Compositions ` within it (i.e., within `INPUT ` Nodes at all levels of - nesting) Note that the InputPorts listed are those of the actual Mechanisms projected to by the ones listed + external_input_ports_of_all_input_nodes : list[InputPort] + a list of all `external InputPort ` of all `INPUT ` + `Nodes ` of the Composition, including any that in `nested Compositions + ` within it (i.e., within `INPUT ` Nodes at all levels of nesting). + Note that the InputPorts listed are those of the actual Mechanisms projected to by the ones listed in `external_input_ports `. external_input_shape : list[1d array] - a list of the `input_shape `\\s of all of the InputPorts listed in `external_input_ports - ` (and are the same as the shapes of those listed in + a list of the `input_shape `\\s of all of the InputPorts listed in + `external_input_ports ` (and are the same as the shapes of those listed in `external_input_ports_of_all_input_nodes `); any input to the Composition must be compatible with these, whether received from the **inputs** argument of one of the - Composition's`execution methods ` or, if it is a `nested Composition + Composition's `execution methods ` or, if it is a `nested Composition `, from the enclosing Composition. external_input_variables : list[2d array] diff --git a/psyneulink/library/components/mechanisms/processing/objective/comparatormechanism.py b/psyneulink/library/components/mechanisms/processing/objective/comparatormechanism.py index 0e3d9b63310..50381e88984 100644 --- a/psyneulink/library/components/mechanisms/processing/objective/comparatormechanism.py +++ b/psyneulink/library/components/mechanisms/processing/objective/comparatormechanism.py @@ -224,9 +224,9 @@ class ComparatorMechanism(ObjectiveMechanism): (see `ComparatorMechanism_Structure` for additional details). function : CombinationFunction, function or method - used to compare the `sample` with the `target`. It can be any PsyNeuLink `CombinationFunction`, - or a python function that takes a 2d array with two items and returns a 1d array of the same length - as the two input items. + used to compare the `sample` with the `target`. It can be any `CombinationFunction `, + or a python function that takes a 2d array with two items and returns a 1d array of the same length as the + two input items. output_port : OutputPort contains the `primary ` OutputPort of the ComparatorMechanism; the default is diff --git a/setup.cfg b/setup.cfg index f4bb89a9ded..9bc862ce6b8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,7 @@ markers = llvm: Tests using LLVM runtime compiler cuda: Tests using LLVM runtime compiler and CUDA GPGPU backend control: Tests including control mechanism and/or control projection + state_features: Tests for OptimizationControlMechanism state_features specifications projection nested: Tests including nested compositions function: Tests of Function classes diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 6286ce09df7..3a01aac00d1 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -9,7 +9,7 @@ from psyneulink.core.globals.sampleiterator import SampleIterator, SampleIteratorError, SampleSpec from psyneulink.core.globals.utilities import _SeededPhilox - +@pytest.mark.control class TestControlSpecification: # These test the coordination of adding a node with a control specification to a Composition # with adding a controller that may also specify control of that node. @@ -141,6 +141,7 @@ def test_objective_mechanism_spec_as_monitor_for_control_error(self): error_msg = error.value.error_value assert expected_error in error_msg + @pytest.mark.state_features @pytest.mark.parametrize("control_spec", [CONTROL, PROJECTIONS]) @pytest.mark.parametrize("state_features_arg", [ 'list', @@ -283,6 +284,7 @@ def test_deferred_init(self, control_spec, state_features_arg): np.testing.assert_allclose(comp.results[trial], expected_results_array[trial], atol=1e-08, err_msg='Failed on expected_output[{0}]'.format(trial)) + @pytest.mark.state_features @pytest.mark.parametrize('state_features_option', [ 'list', 'set', @@ -665,7 +667,7 @@ def test_transfer_mechanism_and_ocm_variations( else: assert 'a[intercept] ControlSignal' not in ocm.control.names - +@pytest.mark.control class TestControlMechanisms: # id, agent_rep, state_feat, mon_for_ctl, allow_probes, obj_mech err_type, error_msg @@ -803,6 +805,7 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c assert err.value.error_value == err_msg messages = [ + # 0 f"There are fewer '{pnl.STATE_FEATURES}' specified for 'OptimizationControlMechanism-0' than the number " f"of InputPort's for all of the INPUT Nodes of its agent_rep ('OUTER COMP'); the remaining inputs will be " @@ -869,33 +872,44 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c # 12 f"The '{pnl.STATE_FEATURES}' argument for 'OptimizationControlMechanism-0' has one or more items in the " f"list specified for 'SHADOW_INPUTS' ('IA') that are not (part of) any INPUT Nodes of its 'agent_rep' " - f"('OUTER COMP')." + f"('OUTER COMP').", + + # 13 + f"'OptimizationControlMechanism-0' has '{pnl.STATE_FEATURES}' specified " + f"(['Shadowed input of EXT[InputPort-0]', " + f"'Shadowed input of EXT[InputPort-0]-1', " + f"'Shadowed input of EXT[InputPort-0]-2']) " + f"that are missing from 'OUTER COMP' and any Compositions nested within it." ] state_feature_args = [ - ('partial_legal_list_spec', messages[0], None, UserWarning), - ('full_list_spec', None, None, None), - ('list_spec_with_none', None, None, None), - ('input_dict_spec', None, None, None), - ('input_dict_spec_short', None, None, None), - ('set_spec_short', None, None, None), - ('set_spec', None, None, None), - ('set_spec_port', None, None, None), - ('no_specs', None, None, None), - ('shadow_inputs_dict_spec', None, None, None), - ('shadow_inputs_dict_spec_w_none', None, None, None), - ('misplaced_shadow', messages[1], None, pnl.CompositionError), - ('ext_shadow', messages[2], None, pnl.OptimizationControlMechanismError), - ('ext_output_port', messages[3], None, pnl.OptimizationControlMechanismError), - ('input_format_wrong_shape', messages[4], None, pnl.OptimizationControlMechanismError), - ('too_many_inputs_warning', messages[5], None, UserWarning), - ('too_many_w_node_not_in_composition_warning', messages[6], None, UserWarning), - ('too_many_inputs_error', messages[7], None, pnl.OptimizationControlMechanismError), - ('bad_dict_spec_warning', messages[8], None, UserWarning), - ('bad_dict_spec_error', messages[8], None, pnl.OptimizationControlMechanismError), - ('bad_shadow_inputs_dict_spec_error', messages[12], None, pnl.OptimizationControlMechanismError), - ('comp_in_list_spec', messages[10], None, pnl.OptimizationControlMechanismError), - ('comp_in_shadow_inupts_spec', messages[11], None, pnl.OptimizationControlMechanismError) + ('single_none_spec', None, None), + ('single_shadow_spec', None, None), + ('single_tuple_shadow_spec', None, None), + ('partial_legal_list_spec', messages[0], UserWarning), + ('full_list_spec', None, None), + ('list_spec_with_none', None, None), + ('input_dict_spec', None, None), + ('input_dict_spec_short', None, None), + ('set_spec_short', None, None), + ('set_spec', None, None), + ('set_spec_port', None, None), + ('no_specs', None, None), + ('shadow_inputs_dict_spec', None, None), + ('shadow_inputs_dict_spec_w_none', None, None), + ('misplaced_shadow', messages[1], pnl.CompositionError), + ('ext_shadow', messages[2], pnl.OptimizationControlMechanismError), + ('ext_output_port', messages[3], pnl.OptimizationControlMechanismError), + ('input_format_wrong_shape', messages[4], pnl.OptimizationControlMechanismError), + ('too_many_inputs_warning', messages[5], UserWarning), + ('too_many_w_node_not_in_composition_warning', messages[6], UserWarning), + ('too_many_inputs_error', messages[7], pnl.OptimizationControlMechanismError), + ('bad_single_spec', messages[13], pnl.OptimizationControlMechanismError), + ('bad_dict_spec_warning', messages[8], UserWarning), + ('bad_dict_spec_error', messages[8], pnl.OptimizationControlMechanismError), + ('bad_shadow_inputs_dict_spec_error', messages[12], pnl.OptimizationControlMechanismError), + ('comp_in_list_spec', messages[10], pnl.OptimizationControlMechanismError), + ('comp_in_shadow_inupts_spec', messages[11], pnl.OptimizationControlMechanismError) ] if len(state_feature_args) != 23: @@ -903,6 +917,7 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c print("*** RESTORE state_feature_args IN test_ocm_state_feature_specs_and_warnings_and_errors() *****") print("***********************************************************************************************") + @pytest.mark.state_features @pytest.mark.control @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x[0] for x in state_feature_args]) @pytest.mark.parametrize('obj_mech', ['obj_mech', 'mtr_for_ctl', None]) @@ -910,9 +925,8 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg """See test_nested_composition_as_agent_rep() for additional tests of state_features specification.""" test_condition = state_feature_args[0] - message_1 = state_feature_args[1] - message_2 = state_feature_args[2] - exception_type = state_feature_args[3] + error_or_warning_message = state_feature_args[1] + exception_type = state_feature_args[2] ia = pnl.ProcessingMechanism(name='IA') ib = pnl.ProcessingMechanism(name='IB') @@ -929,16 +943,19 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg state_features_dict = { # Legal state_features specifications - 'partial_legal_list_spec': [oa.output_port], # Note: only specifies ia; oa and ob assigned default inputs + 'single_none_spec': None, + 'single_shadow_spec': pnl.SHADOW_INPUTS, + 'single_tuple_shadow_spec': (pnl.SHADOW_INPUTS, pnl.Logistic), + 'partial_legal_list_spec': [oa.output_port], # only specifies ia; oa and ob assigned default inputs 'full_list_spec': [ia.input_port, oa.output_port, [3,1,2]], 'list_spec_with_none': [ia.input_port, None, [3,1,2]], - 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: use icomp & out of order is OK - 'input_dict_spec_short': {ob:ob.output_port, oa:oc.input_port}, # Note: missing ia spec and out of order + 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # use icomp & out of order is OK + 'input_dict_spec_short': {ob:ob.output_port, oa:oc.input_port}, # missing ia spec and out of order 'set_spec_short': {oa}, - 'set_spec': {ob, icomp, oa}, # Note: out of order is OK, and use of Nested comp as spec + 'set_spec': {ob, icomp, oa.input_port}, # out of order, use of Nested comp and InputPort as specs all OK 'set_spec_port': {ob.input_port, icomp, oa}, - 'no_specs': None, - 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, # <- ia & ob OK BECAUSE JUST FOR SHADOWING + 'no_specs': 'THIS IS IGNORED', + 'shadow_inputs_dict_spec': {pnl.SHADOW_INPUTS:[ia, oa, ob]}, # ia & ob OK because just for shadowing 'shadow_inputs_dict_spec_w_none': {pnl.SHADOW_INPUTS:[ia, None, ob]}, # Illegal state_features specifications @@ -949,6 +966,8 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'too_many_inputs_warning': [ia.input_port, oa.output_port, ob.output_port, oc.output_port], 'too_many_inputs_error': [ia.input_port, oa.output_port, ob.output_port, oc.output_port], 'too_many_w_node_not_in_composition_warning': [ia, oa, ob, ext], + 'bad_single_spec': ext.input_port, + 'bad_single_numeric_spec': [3], 'bad_dict_spec_warning': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # oc is not an INPUT Node 'bad_dict_spec_error': {oa:oc.input_port, ia:ia, oc:ob.output_port}, # ia & oc are not *ocomp* INPUT Nodes 'bad_shadow_inputs_dict_spec_error': {pnl.SHADOW_INPUTS:[ia.output_port, None, ob]}, # not INPUT InputPort @@ -983,6 +1002,37 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ocomp.add_controller(ocm) ocomp.run() + if test_condition == 'single_none_spec': + assert len(ocm.state_input_ports) == 0 + assert ocm.state_features == {ia.input_port: None, + oa.input_port: None, + ob.input_port: None} + assert ocm.state_feature_values == {} + + if test_condition == 'single_shadow_spec': + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OA[InputPort-0]', + 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: oa.input_port, + ob.input_port: ob.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {ia.input_port: [0.], + oa.input_port: [0.], + ob.input_port: [0., 0., 0.]} + + if test_condition == 'single_tuple_shadow_spec': + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OA[InputPort-0]', + 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_features == {ia.input_port: ia.input_port, + oa.input_port: oa.input_port, + ob.input_port: ob.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {ia.input_port: [0.5], + oa.input_port: [0.5], + ob.input_port: [0.5, 0.5, 0.5]} + assert all('Logistic' in port.function.name for port in ocm.state_input_ports) + if test_condition == 'full_list_spec': assert len(ocm.state_input_ports) == 3 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', @@ -1017,10 +1067,11 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg [[0.], [0.], [0, 0, 0]])) elif test_condition == 'input_dict_spec_short': - assert len(ocm.state_input_ports) == 2 - assert ocm.state_input_ports.names == ['Shadowed input of OC[InputPort-0]', + assert len(ocm.state_input_ports) == 3 + assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', + 'Shadowed input of OC[InputPort-0]', 'OB[OutputPort-0]'] - assert ocm.state_features == {ia.input_port: None, + assert ocm.state_features == {ia.input_port: ia.input_port, oa.input_port: oc.input_port, ob.input_port: ob.output_port} assert all(np.allclose(expected, actual) @@ -1092,7 +1143,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'bad_dict_spec_warning'}: with pytest.warns(UserWarning) as warning: ocomp.add_controller(ocm) - assert message_1 in [warning[i].message.args[0] for i in range(len(warning))] + assert error_or_warning_message in [warning[i].message.args[0] for i in range(len(warning))] else: with pytest.warns(UserWarning) as warning: ocomp.add_controller(ocm) @@ -1106,18 +1157,215 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_features == {ia.input_port: oa.output_port, oa.input_port: oa.input_port, ob.input_port: ob.input_port} - assert message_1 in [warning[i].message.args[0] for i in range(len(warning))] + assert error_or_warning_message in [warning[i].message.args[0] for i in range(len(warning))] else: with pytest.raises(exception_type) as error: ocomp.add_controller(ocm) ocomp.run() - assert message_1 in str(error.value) + assert error_or_warning_message in str(error.value) + + state_features_arg = [ + 'single_numeric_spec', # <- same numeric input for all INPUT Node InputPorts + 'single_tuple_numeric_spec', # <- same value and function assigned to all INPUT Node InputPorts + 'single_port_spec', # <- same Port for all INPUT Node InputPorts + 'single_mech_spec', # <- same Mech's InputPort for INPUT Node InputPorts + 'nested_partial_set', # <- only one of two INPUT Nodes of nested Comp in set format + 'nested_partial_dict', # <- only one of two INPUT Nodes of nested Comp in dict format + 'nested_partial_list', # <- specify 1st 3 INPUT Node InputPorts; 4th (I2) should get shaddowed + 'nested_full_set', # <- both of two INPUT Nodes of nested Comp in set format + 'nested_full_dict', # <- both of two INPUT Nodes of nested Comp in dict format + 'nested_comp_set', # <- nested Comp as itself in set format + 'nested_comp_dict', # <- nested Comp as itself in dict format with a single spec for all INPUT Nodes + 'no_spec', # <- Assign state_feature_default to all Nodes + 'bad' # <- Mechanism not in agent_rep + ] + @pytest.mark.state_features + @pytest.mark.control + @pytest.mark.composition + @pytest.mark.parametrize('nested_agent_rep', + [(False, 'OUTER COMP'),(True, 'MIDDLE COMP')], + ids=['unnested','nested']) + @pytest.mark.parametrize('state_features_arg', state_features_arg, + ids= [f"state_feature-{x}" for x in state_features_arg] + ) + def test_state_features_in_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_arg): + """Test state_features for agent_rep that is a nested Composition and also has one nested with in. + Also test for single state_feature_spec and INPUT Node with multiple InputPorts, not tested in + test_ocm_state_feature_specs_and_warnings_and_errors() (see that for tests of basic state_features specs). + """ + + I1 = pnl.ProcessingMechanism(name='I1') + I2 = pnl.ProcessingMechanism(name='I2') + icomp = pnl.Composition(nodes=[I1,I2], name='INNER COMP') + A = pnl.ComparatorMechanism(name='A') + B = pnl.ProcessingMechanism(name='B') + C = pnl.ProcessingMechanism(name='C', size=3) + D = pnl.ProcessingMechanism(name='D') + mcomp = pnl.Composition(pathways=[[A,B,C], icomp], name='MIDDLE COMP') + ocomp = pnl.Composition(nodes=[mcomp], name='OUTER COMP') + + # Test args: + if nested_agent_rep is True: + agent_rep = mcomp + error_text = f"'OCM' has '{STATE_FEATURES}' specified (['D[OutputPort-0]']) that are missing from both " \ + f"its `agent_rep` ('{nested_agent_rep[1]}') as well as 'OUTER COMP' and any " \ + f"Compositions nested within it." + else: + agent_rep = None + error_text = '"\'OCM\' has \'state_features\' specified ([\'D[OutputPort-0]\']) that are ' \ + 'missing from \'OUTER COMP\' and any Compositions nested within it."' + + state_features = { + 'single_numeric_spec': [3], + 'single_tuple_numeric_spec': ([3], pnl.Linear(slope=5)), + 'single_port_spec': I1.output_port, + 'single_mech_spec': I1, + 'nested_partial_list': [I1.output_port, [2], I2], + 'nested_partial_set': {A.input_ports[pnl.SAMPLE], I2}, + 'nested_full_set': {A, I1, I2}, + 'nested_partial_dict': {A.input_ports[pnl.SAMPLE]:[3.5], I2:I1.input_port}, + 'nested_full_dict': {A:A.input_port, I1:I2.input_port, I2:I1.input_port}, + 'nested_comp_set': {mcomp}, + 'nested_comp_dict': {mcomp: I1}, + 'no_spec': 'SHOULD ASSIGN NONE TO ALL INPUT Node InputPorts', + 'bad': [D.output_port] + }[state_features_arg] + + if state_features_arg == 'nested_partial_set': + state_feature_default = None # Test assignment of SHADOW_INPUTS to specs in set, and None to others + else: + state_feature_default = pnl.SHADOW_INPUTS + + if state_features_arg == 'no_spec': + ocm = pnl.OptimizationControlMechanism(name='OCM', + agent_rep=agent_rep, + objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), + allow_probes=True, + function=pnl.GridSearch(), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I1), + allocation_samples=[10, 20, 30])) + else: + ocm = pnl.OptimizationControlMechanism(name='OCM', + agent_rep=agent_rep, + state_features=state_features, + state_feature_default=state_feature_default, + objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), + allow_probes=True, + function=pnl.GridSearch(), + control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I1), + allocation_samples=[10, 20, 30])) + if state_features_arg == 'bad': + with pytest.raises(pnl.OptimizationControlMechanismError) as error: + ocomp.add_controller(ocm) + ocomp.run() + assert error_text in str(error.value) + else: + ocomp.add_controller(ocm) + ocomp.run() + + if state_features_arg == 'single_numeric_spec': + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: [3], + A.input_ports[pnl.TARGET]: [3], + I1.input_port: [3], + I2.input_port: [3]} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [3], + A.input_ports[pnl.TARGET]: [3], + I1.input_port: [3], + I2.input_port: [3]} + + elif state_features_arg == 'single_tuple_numeric_spec': + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: [3], + A.input_ports[pnl.TARGET]: [3], + I1.input_port: [3], + I2.input_port: [3]} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [15], + A.input_ports[pnl.TARGET]: [15], + I1.input_port: [15], + I2.input_port: [15]} + + elif state_features_arg in {'single_port_spec'}: + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: I1.output_port, + A.input_ports[pnl.TARGET]: I1.output_port, + I1.input_port: I1.output_port, + I2.input_port: I1.output_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], + A.input_ports[pnl.TARGET]: [0], + I1.input_port: [0], + I2.input_port: [0]} + + elif state_features_arg in {'single_mech_spec'}: + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: I1.input_port, + A.input_ports[pnl.TARGET]: I1.input_port, + I1.input_port: I1.input_port, + I2.input_port: I1.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], + A.input_ports[pnl.TARGET]: [0], + I1.input_port: [0], + I2.input_port: [0]} + + elif state_features_arg in 'nested_partial_list': + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: I1.output_port, + A.input_ports[pnl.TARGET]: [2], + I1.input_port: I2.input_port, + I2.input_port: I2.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], + A.input_ports[pnl.TARGET]: [2], + I1.input_port: [0], + I2.input_port: [0]} + + elif state_features_arg in 'nested_partial_set': + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: A.input_ports[pnl.SAMPLE], + A.input_ports[pnl.TARGET]: None, + I1.input_port: None, + I2.input_port: I2.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], + I2.input_port: [0]} + + elif state_features_arg == 'nested_partial_dict': + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: [3.5], + A.input_ports[pnl.TARGET]: A.input_ports[pnl.TARGET], + I1.input_port: I1.input_port, + I2.input_port: I1.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [3.5], + A.input_ports[pnl.TARGET]: [0], + I1.input_port: [0], + I2.input_port: [0]} + + elif state_features_arg == 'nested_full_set': + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: A.input_ports[pnl.SAMPLE], + A.input_ports[pnl.TARGET]: A.input_ports[pnl.TARGET], + I1.input_port: I1.input_port, + I2.input_port: I2.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], + A.input_ports[pnl.TARGET]: [0], + I1.input_port: [0], + I2.input_port: [0]} + + elif state_features_arg == 'nested_full_dict': + assert ocm.state_features == {A.input_port: A.input_port, + A.input_ports[pnl.TARGET]: A.input_port, + I1.input_port: I2.input_port, + I2.input_port: I1.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[0]: [0], + A.input_ports[1]: [0], + I1.input_port: [0], + I2.input_port: [0]} + + elif state_features_arg == 'nested_comp_dict': + assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: I1.input_port, + A.input_ports[pnl.TARGET]: I1.input_port, + I1.input_port: I1.input_port, + I2.input_port: I1.input_port} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], + A.input_ports[pnl.TARGET]: [0], + I1.input_port: [0], + I2.input_port: [0]} @pytest.mark.control @pytest.mark.parametrize('state_fct_assignments', [ - 'partial_w_dict', - 'partial_w_params_dict', + # 'partial_w_dict', + # 'partial_w_params_dict', 'tuple_override_dict', 'tuple_override_params_dict', 'port_spec_dict_in_feat_dict', @@ -1148,7 +1396,8 @@ def test_state_feature_function_specs(self, state_fct_assignments): state_feature_function = fct_c elif state_fct_assignments == 'tuple_override_dict': state_features = [({pnl.PROJECTIONS: A, - pnl.FUNCTION: pnl.Buffer}, fct_a), + pnl.FUNCTION: pnl.Buffer}, + fct_a), (B, fct_b), C] state_feature_function = fct_c @@ -1200,6 +1449,7 @@ def test_state_feature_function_specs(self, state_fct_assignments): for actual, expected in zip(list(ocm.parameters.state_feature_values.get('test').values()), [[2],[2],[2]])) + @pytest.mark.state_features @pytest.mark.control def test_ocm_state_and_state_dict(self): ia = pnl.ProcessingMechanism(name='IA') @@ -2110,6 +2360,7 @@ def test_modulation_of_random_state(self, comp_mode, num_generators): assert np.allclose(best_second, comp.results[1]) +@pytest.mark.control class TestModelBasedOptimizationControlMechanisms_Execution: def test_ocm_default_function(self): a = pnl.ProcessingMechanism() @@ -3270,111 +3521,8 @@ def test_input_CIM_assignment(self, comp_mode): # -7 ((5*-1)+(-2*1)) assert np.allclose(results, [[7]]) - state_features_arg = [ - 'nested_partial_set', # <- Specify only one of two INPUT Nodes of nested comp in set format - 'nested_partial_dict', # <- Specify only one of two INPUT Nodes of nested comp in dict format - 'nested_full_set', # <- Specify both of two INPUT Nodes of nested comp in set format - 'nested_full_dict', # <- Specify both of two INPUT Nodes of nested comp in dict format - 'nested_comp_set', # <- Specify nested comp as itself in set format - 'nested_comp_dict', # <- Specify nested comp as itself in dict format with a single spec for all INPUT Nodes - 'no_spec', # <- Assign default state_features - 'bad' # <- Specify Mechanism not in agent_rep - ] - @pytest.mark.control - @pytest.mark.composition - @pytest.mark.parametrize('nested_agent_rep', - [(False, 'OUTER COMP'),(True, 'MIDDLE COMP')], - ids=['unnested','nested']) - @pytest.mark.parametrize('state_features_arg', state_features_arg, - ids= [f"state_feature-{x}" for x in state_features_arg] - ) - def test_nested_composition_as_agent_rep(self, nested_agent_rep, state_features_arg): - """Also tests state_features for comp nested within nested_agent_rep. - See test_ocm_state_feature_specs_and_warnings_and_errors() for additional tests of state_features specification. - """ - - I1 = pnl.ProcessingMechanism(name='I1') - I2 = pnl.ProcessingMechanism(name='I2') - icomp = pnl.Composition(nodes=[I1,I2], name='INNER COMP') - A = pnl.ProcessingMechanism(name='A') - B = pnl.ProcessingMechanism(name='B') - C = pnl.ProcessingMechanism(name='C', size=3) - D = pnl.ProcessingMechanism(name='D') - mcomp = pnl.Composition(pathways=[[A,B,C], icomp], name='MIDDLE COMP') - ocomp = pnl.Composition(nodes=[mcomp], name='OUTER COMP') - - # Test args: - if nested_agent_rep is True: - agent_rep = mcomp - error_text = f"'OCM' has '{STATE_FEATURES}' specified (['D[OutputPort-0]']) that are missing from both " \ - f"its `agent_rep` ('{nested_agent_rep[1]}') as well as 'OUTER COMP' and any " \ - f"Compositions nested within it." - else: - agent_rep = None - error_text = '"\'OCM\' has \'state_features\' specified ([\'D[OutputPort-0]\']) that are ' \ - 'missing from \'OUTER COMP\' and any Compositions nested within it."' - - if state_features_arg == 'nested_partial_set': - state_features = {A, I2} - elif state_features_arg == 'nested_full_set': - state_features = {A, I1, I2} - elif state_features_arg == 'nested_partial_dict': - state_features = {A:A.input_port, I2:I1.input_port} - elif state_features_arg == 'nested_full_dict': - state_features = {A:A.input_port, I1:I2.input_port, I2:I1.input_port} - elif state_features_arg == 'nested_comp_set': - state_features = {mcomp} - elif state_features_arg == 'nested_comp_dict': - state_features = {mcomp:I1} - elif state_features_arg == 'bad': - state_features = [D.output_port] - - if state_features_arg == 'no_spec': - ocm = pnl.OptimizationControlMechanism(name='OCM', - agent_rep=agent_rep, - objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), - allow_probes=True, - function=pnl.GridSearch(), - control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I1), - allocation_samples=[10, 20, 30])) - else: - ocm = pnl.OptimizationControlMechanism(name='OCM', - agent_rep=agent_rep, - state_features=state_features, - objective_mechanism=pnl.ObjectiveMechanism(monitor=[B]), - allow_probes=True, - function=pnl.GridSearch(), - control_signals=pnl.ControlSignal(modulates=(pnl.SLOPE,I1), - allocation_samples=[10, 20, 30])) - if state_features_arg == 'bad': - with pytest.raises(pnl.OptimizationControlMechanismError) as error: - ocomp.add_controller(ocm) - ocomp.run() - assert error_text in str(error.value) - else: - ocomp.add_controller(ocm) - ocomp.run() - if state_features_arg == 'nested_partial_set': - assert ocm.state_features == {A.input_port: A.input_port, - I1.input_port: None, - I2.input_port: I2.input_port} - elif state_features_arg == 'nested_partial_dict': - assert ocm.state_features == {A.input_port: A.input_port, - I1.input_port: None, - I2.input_port: I1.input_port} - elif state_features_arg == 'nested_full_dict': - assert ocm.state_features == {A.input_port: A.input_port, - I1.input_port: I2.input_port, - I2.input_port: I1.input_port} - elif state_features_arg == 'nested_comp_dict': - assert ocm.state_features == {A.input_port:I1.input_port, - I1.input_port: I1.input_port, - I2.input_port: I1.input_port} - else: - assert ocm.state_features == {A.input_port: A.input_port, - I1.input_port: I1.input_port, - I2.input_port: I2.input_port} +@pytest.mark.control class TestSampleIterator: def test_int_step(self): @@ -3488,6 +3636,7 @@ def test_list(self): assert sample_iterator.num == len(sample_list) +@pytest.mark.control class TestControlTimeScales: def test_time_step_before(self): From 4f0f65292e1ea1d4c67ba1ab351eb5977e8cde66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Mar 2022 04:41:09 +0000 Subject: [PATCH 251/285] requirements: update graph-scheduler requirement (#2343) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 42b0dadb2ae..d0b01778816 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ autograd<=1.3 -graph-scheduler>=0.2.0, <1.0.1 +graph-scheduler>=0.2.0, <1.1.1 dill<=0.32 elfi<0.8.4 graphviz<0.20.0 From 639751334a524fc59cef8224a112e11ef69c45b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Mar 2022 20:33:22 +0000 Subject: [PATCH 252/285] github-actions(deps): bump actions/cache from 2.1.7 to 3 (#2353) --- .github/workflows/pnl-ci-docs.yml | 2 +- .github/workflows/pnl-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pnl-ci-docs.yml b/.github/workflows/pnl-ci-docs.yml index 15eaf6f60a4..f2396ef7a04 100644 --- a/.github/workflows/pnl-ci-docs.yml +++ b/.github/workflows/pnl-ci-docs.yml @@ -79,7 +79,7 @@ jobs: echo ::set-output name=pip_cache_dir::$(python -m pip cache dir) - name: Wheels cache - uses: actions/cache@v2.1.7 + uses: actions/cache@v3 with: path: ${{ steps.pip_cache.outputs.pip_cache_dir }}/wheels key: ${{ runner.os }}-python-${{ matrix.python-version }}-${{ matrix.python-architecture }}-pip-wheels-v2-${{ github.sha }} diff --git a/.github/workflows/pnl-ci.yml b/.github/workflows/pnl-ci.yml index 4e1088a429b..25227973e1d 100644 --- a/.github/workflows/pnl-ci.yml +++ b/.github/workflows/pnl-ci.yml @@ -68,7 +68,7 @@ jobs: echo ::set-output name=pip_cache_dir::$(python -m pip cache dir) - name: Wheels cache - uses: actions/cache@v2.1.7 + uses: actions/cache@v3 with: path: ${{ steps.pip_cache.outputs.pip_cache_dir }}/wheels key: ${{ runner.os }}-python-${{ matrix.python-version }}-${{ matrix.python-architecture }}-pip-wheels-v2-${{ github.sha }} From 7a406ca0f9e6d37e29d6579c724219abb0bafbc4 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 23 Mar 2022 12:02:56 -0400 Subject: [PATCH 253/285] Refactor/ocm/state features all as input ports (#2355) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * - * • Passes all tests except test_json with 'model_with_control' * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * - * - * - * - * - * - * - * - * • inputport.py: - add figure for shadowing * - * - * • processingmechanism.py: - __init__(): modify typecheck for input_ports to allow single items * • optimizationcontrolmechanism.py - allow single spec (None, array, tuple, or Components) that is assigned to all INPUT Node InputPorts - state_feature_default is assigned to all unspecified INPUT Node InputPorts (for a list that is shorter than the number, a dict or a set that has fewer, or any that are added to agent_rep after controller is initially constructed and added to Composition) * - * - * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * - * - * - * - * - * • optimizationcontrolmechanism.py: - _state_feature_values_getter(): for numeric state_feature, return state_input_port.functio(numeric_value) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - added tests for single state_feature spec * - composition.py, inputport.py, optimizationcontrolmechanism.py: docstring mods re: "external InputPort" * - * - * - * • test_control.py: - test_state_features_in_nested_composition_as_agent_rep(): - add tests for single state_feature specs - add tests for INPUT Node with more than on InputPort * - * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * • optimizationcontrolmechanism.py: IN PROGRESS - _specified_INPUT_Node_InputPorts_in_order: use name (str) of items not yet in agent_rep rather than 'None' - state_features & state_feature_values: - use name (str) as key if INPUT Node InputPort is not yet in agent_rep - use name (str) if source (spec) is not yet in Composition * - * • optimizationcontrolmechanism.py: IMPLEMENTED - state_features: - convert all keys and values to port.full_name - if not yet in comp or agent_rep, add "DEFERRED..." * • optimizationcontrolmechanism.py: PASSES deferred_init and partial_deferred_init tests * • optimizationcontrolmechanism.py: - state_feature_values: strings for missing keys or values * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_ocm_state_feature_specs_and_warnings_and_errors * • test_control.py: PASSING all state_feature tests * • composition.py: add _is_in_composition method * - * - * - * - * - * - * - * - * - Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- .../control/optimizationcontrolmechanism.py | 394 +++++++++++------- psyneulink/core/components/ports/inputport.py | 2 +- psyneulink/core/compositions/composition.py | 20 +- tests/composition/test_composition.py | 4 +- tests/composition/test_control.py | 263 +++++++----- 5 files changed, 411 insertions(+), 272 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index dced74d4f18..54f17e486b6 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -427,12 +427,22 @@ * *Mechanism* -- create a `state_input_port ` that `shadows ` the input to the `primary InputPort ` of the specified Mechanism (this is the same as explicitly specifying the Mechanism's input_port, as described `above - `). If the Mechanism is in a `nested Composition + `). If the Mechanism is in a `nested Composition `, it must be an `INPUT ` `Node ` of that Composition (see `note ` above). If the Mechanism's `OutputPort` needs to be used, it must be specified explicitly (as described `above `). + .. note:: + The use of a Mechanism to specify the shadowing of its `primary InputPort ` is unique to + its specification in the **state_features** argument of an OptimizationControlMechanism, and differs from the + ordinary usage where it specifies a Projection from its `primary OutputPort ` (see + `InputPort specification `). This difference extends to the use + of a Mechanism in the *PROJECTIONS* entry of an `InputPort specification dictionary + ` used in the **state_features** argument, where there too + it designates shadowing of its `primary InputPort ` rather than a `Projection` from its + `primary OutputPort `. + .. _OptimizationControlMechanism_Tuple_State_Feature: * *2-item tuple* -- the first item must be any of the forms of individual state_feature specifications @@ -481,7 +491,7 @@ * **state_feature_function** -- specifies a `function ` to be used as the default function for `state_input_ports `. This is assigned as the `function ` to any state_input_ports for which *no other* `Function` is specified -- - that is, in either an InputPort specification dictionary ` or a `2-item tuple + that is, in either an `InputPort specification dictionary ` or a `2-item tuple ` in the **state_features** argument (see `state_features `). If either of the latter is specified, they override the specification in **state_feature_function**. If **state_feature_function** is *not* specified, then @@ -1016,6 +1026,7 @@ import copy import warnings from collections.abc import Iterable +from typing import Union import numpy as np import typecheck as tc @@ -1059,12 +1070,25 @@ RANDOM_VARIABLES = 'random_variables' NUM_ESTIMATES = 'num_estimates' + +def deferred_state_feature_node_msg(node_name, agent_rep_name): + return f"DEFERRED {node_name} OF {agent_rep_name}" + +def deferred_state_feature_spec_msg(spec_str, comp_name): + return f"{spec_str} NOT (YET) IN {comp_name}" + +def not_specified_state_feature_spec_msg(spec_str, comp_name): + return f"NO SPECIFICATION (YET) FOR {spec_str} IN {comp_name}" + def _state_feature_values_getter(owning_component=None, context=None): """Return dict {agent_rep INPUT Node InputPort: value} suitable for **predicted_inputs** arg of evaluate method. Only include entries for sources specified in **state_features**, corresponding to OCM's state_input_ports; default input will be assigned for all other INPUT Node InputPorts in composition._instantiate_input_dict(). """ + # FIX: REFACTOR TO DO VALIDATIONS ON INIT AND INITIAL RUN-TIME CHECK + # AND SIMPLY RETURN VALUES (WHICH SHOULD ALL BE ASSIGNED BY RUN TIME) DURING EXECUTION / SIMULATIONS + # If no state_input_ports return empty list if (not owning_component.num_state_input_ports): return {} @@ -1076,14 +1100,16 @@ def _state_feature_values_getter(owning_component=None, context=None): in zip(owning_component._specified_INPUT_Node_InputPorts_in_order, owning_component.state_feature_specs) if spec is not None] + num_agent_rep_input_ports = len(owning_component._get_agent_rep_input_receivers()) + assert len(specified_state_features) == \ len(specified_INPUT_Node_InputPorts) == \ owning_component.num_state_input_ports # If OptimizationControlMechanism is still under construction, use items from input_values as placemarkers - if context.source == ContextFlags.CONSTRUCTOR: - return {k:v for k,v in zip(specified_INPUT_Node_InputPorts, - owning_component.input_values[owning_component.num_outcome_input_ports:])} + # if context.source == ContextFlags.CONSTRUCTOR: + # return {k:v for k,v in zip(specified_INPUT_Node_InputPorts, + # owning_component.input_values[owning_component.num_outcome_input_ports:])} # Construct state_feature_values dict state_feature_values = {} @@ -1092,26 +1118,34 @@ def _state_feature_values_getter(owning_component=None, context=None): state_input_port = owning_component.state_input_ports[i] spec = specified_state_features[i] - # FIX: 3/18/22: REMOVE THIS TO ALLOW INCLUSION OF None SPECS IN DICT: - # state_input_port not (yet) specified; default input will be assigned in _instantiate_input_dict() - if not isinstance(key, InputPort) or spec is None: - continue - # # FIX: 3/18/22 - RESTORE? - # elif spec is None: - # # # FIX: ??TRY IGNORING RATHER THAN ASSIGNING, AS IT WILL BE ASSIGNED IN _instantiate_input_dict() - # # MODIFIED 3/18/22 OLD: - # state_feature_value = state_input_port.default_input_shape - # # MODIFIED 3/18/22 NEW: - # # continue - # # MODIFIED 3/18/22 END + # Get key + if not isinstance(key, InputPort): + # INPUT Node InputPort is not fully or properly specified + key = deferred_state_feature_node_msg((key or str(i - num_agent_rep_input_ports)), + owning_component.agent_rep.name) + elif key not in owning_component._get_agent_rep_input_receivers(): + # INPUT Node InputPort is not (yet) in agent_rep + key = deferred_state_feature_node_msg(key.full_name, owning_component.agent_rep.name) + + # Get state_feature_value + if spec is None: + # state_feature not specified; default input will be assigned in _instantiate_input_dict() + state_feature_value = not_specified_state_feature_spec_msg((key if isinstance(key, str) else key.full_name), + owning_component.composition.name) elif is_numeric(spec): + # if spec is numeric, use that state_feature_value = state_input_port.function(spec) + elif (hasattr(owning_component, 'composition') + and not owning_component.composition._is_in_composition(spec)): + # spec is not in ocm.composition + state_feature_value = deferred_state_feature_spec_msg(spec.full_name, owning_component.agent_rep.name) elif state_input_port.parameters.value._get(context) is not None: + # if state_input_port returns a value, use that state_feature_value = state_input_port.parameters.value._get(context) else: + # otherwise use state_input_port's default input value state_feature_value = state_input_port.default_input_shape - # state_feature_values[key] = convert_to_np_array(state_feature_value) state_feature_values[key] = state_feature_value return state_feature_values @@ -1914,7 +1948,7 @@ def _validate_input_nodes(self, nodes, enforce=None): else: warnings.warn(message) - # FIX: 1/29/22 - REFACTOR TO SUPPORT TUPLE AND InportPort SPECIFICATION DICT FOR MULT. PROJS. TO STATE_INPUT_PORT + # FIX: 1/29/22 - REFACTOR TO SUPPORT InportPort SPECIFICATION DICT FOR MULT. PROJS. TO STATE_INPUT_PORT def _parse_state_feature_specs(self, context=None): """Parse entries of state_features specifications used to construct state_input_ports. @@ -1960,7 +1994,7 @@ def _parse_state_feature_specs(self, context=None): - if there are fewer sources listed than INPUT Node external InputPorts, assign state_feature_default to the entries in state_feature_specs corresponding to the remaining INPUT Node external InputPorts - if there more sources listed than INPUT Nodes, leave the excess ones, and label them as - 'EXPECTED INPUT NODE n' for later resolution (see below). + 'EXPECT ' for later resolution (see below). - set {INPUT Node, Input Node...}: specifies INPUT Nodes to be shadowed - every item must be an INPUT Node of agent_rep or an INPUT Node of a nested Composition within it that @@ -2039,18 +2073,47 @@ def expand_nested_input_comp_to_input_nodes(comp): input_nodes.append(node) return input_nodes + def get_port_for_mech_spec(spec:Union[Port,Mechanism]): + """Return port for Mechanism specified as state_feature + This is used to override the standard interpretation of a Mechanism in an InputPort specification: + - return Primary InputPort of Mechanism (to be shadowed) if agent_rep is Composition + - return Primary OutputPort of Mechanism (standard behavior) if agent_rep is a CFA + """ + # assert isinstance(mech, Mechanism), \ + # f"PROGRAM ERROR: {mech} should be Mechanism in call to get_port_for_mech_spec() for '{self.name}'" + if isinstance(spec, Port): + return spec + if self.agent_rep_type == COMPOSITION: + # FIX: 11/29/21: MOVE THIS TO _parse_shadow_inputs + # (ADD ARG TO THAT FOR DOING SO, OR RESTRICT TO InputPorts IN GENERAL) + if len(spec.input_ports)!=1: + raise OptimizationControlMechanismError( + f"A Mechanism ({spec.name}) is specified to be shadowed in the '{STATE_FEATURES}' arg " + f"for '{self.name}', but it has more than one {InputPort.__name__}; a specific one of its " + f"{InputPort.__name__}s must be specified to be shadowed.") + return spec.input_port + else: + # agent_rep is a CFA + return spec.output_port + # PARSE SPECS ------------------------------------------------------------------------------------------ - # Generate parallel lists of INPUT Nodes and corresponding feature specs (for sources of inputs) - def _parse_specs(state_feature_specs, spec_type="list"): + # Generate parallel lists of state feature specs (for sources of inputs) + # and INPUT Nodes to which they (if specified in dict or set format) + def _parse_specs(state_feature_specs, specified_input_ports=None, spec_type="list"): """Validate and parse INPUT Node specs assigned to construct state_feature_specs Validate number and identity of specs relative to agent_rep INPUT Nodes. Assign spec for every INPUT Mechanism (nested) within agent_rep (i.e., for all nested Compositions) as entries in state_feature_specs - Return names for use as input_port_names in main body of method + Return names for use as state_input_port_names in main body of method """ + parsed_feature_specs = [] num_specs = len(state_feature_specs) - num_ports = len(agent_rep_input_ports) + num_specified_ports = len(specified_input_ports) + num_agent_rep_input_ports = len(agent_rep_input_ports) + assert num_specs == num_specified_ports, f"ALERT: num state_feature_specs != num ports in _parse_spec()" + # Note: there may be more state_feature_specs (i.e., ones for unspecified input_ports) + # than num_specified_ports if self.agent_rep_type == COMPOSITION: @@ -2058,7 +2121,7 @@ def _parse_specs(state_feature_specs, spec_type="list"): # ALSO, WARNING IS TRIGGERED IF MECHANIMS RATHER THAN ITS INPUT_PORTS ARE SPEC'D # AT THE LEAST, MOVE TO THEIR OWN VALIDATION HELPER METHOD # Too FEW specs for number of agent_rep receivers - if len(self.state_feature_specs) < num_ports: + if len(self.state_feature_specs) < num_agent_rep_input_ports: warnings.warn(f"There are fewer '{STATE_FEATURES}' specified for '{self.name}' than the number " f"of {InputPort.__name__}'s for all of the INPUT Nodes of its {AGENT_REP} " f"('{self.agent_rep.name}'); the remaining inputs will be assigned default values " @@ -2067,7 +2130,7 @@ def _parse_specs(state_feature_specs, spec_type="list"): f"inputs.") # Too MANY specs for number of agent_rep receivers - if num_specs > num_ports: + if num_specs > num_agent_rep_input_ports: # specs_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" # for spec in self._get_specs_not_in_agent_rep(state_feature_specs)] specs_not_in_agent_rep = \ @@ -2079,7 +2142,7 @@ def _parse_specs(state_feature_specs, spec_type="list"): warnings.warn( f"The '{STATE_FEATURES}' specified for {self.name} is associated with a number of " f"{InputPort.__name__}s ({len(state_feature_specs)}) that is greater than for the " - f"{InputPort.__name__}s of the INPUT Nodes ({num_ports}) for the " + f"{InputPort.__name__}s of the INPUT Nodes ({num_agent_rep_input_ports}) for the " f"Composition assigned as its {AGENT_REP} ('{self.agent_rep.name}'), which includes " f"the following that are not (yet) in '{self.agent_rep.name}': {spec_type}. Executing " f"{self.name} before the additional item(s) are added as (part of) INPUT Nodes will " @@ -2088,7 +2151,7 @@ def _parse_specs(state_feature_specs, spec_type="list"): warnings.warn( f"The '{STATE_FEATURES}' specified for {self.name} is associated with a number of " f"{InputPort.__name__}s ({len(state_feature_specs)}) that is greater than for the " - f"{InputPort.__name__}s of the INPUT Nodes ({num_ports}) for the " + f"{InputPort.__name__}s of the INPUT Nodes ({num_agent_rep_input_ports}) for the " f"Composition assigned as its {AGENT_REP} ('{self.agent_rep.name}'). Executing " f"{self.name} before the additional item(s) are added as (part of) INPUT Nodes will " f"generate an error.") @@ -2103,34 +2166,34 @@ def _parse_specs(state_feature_specs, spec_type="list"): f"replaced by direct references to the Mechanisms (or their InputPorts) within them to be " f"shadowed.") - spec_names = [] - self._num_state_feature_specs = max(num_specs, num_ports) + state_input_port_names = [] + self._num_state_feature_specs = max(num_specs, num_agent_rep_input_ports) for i in range(self._num_state_feature_specs): # PORT & PORT_NAME # (and specs for CFA and any Nodes not yet in agent_rep) - spec_name = None + state_input_port_name = None state_feature_fct = None if self.agent_rep_type == COMPOSITION: # Process number of specs for which there are known INPUT Ports of agent_rep - if i < num_ports: + if i < num_agent_rep_input_ports: # Just get port and port name (spec will be parsed and assigned below) # Node should be in agent_rep, so use that to be sure if self.agent_rep_type == COMPOSITION: port = agent_rep_input_ports[i] port_name = port.full_name - # For Nodes not (yet) in agent_rep, so + # For Nodes not (yet) in agent_rep: else: # - get specified value for spec, for later parsing and assignment (once Node is known) spec = state_feature_specs[i] - port = None + port = specified_input_ports[i] # - assign "DEFERRED n" as node name - port_name = f'DEFFERED {str(i-num_ports)}' + state_input_port_name = f'DEFFERED {str(i-num_agent_rep_input_ports)}' # For CompositionFunctionApproximator, assign spec as port else: spec = state_feature_specs[i] port = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner - port_name = f"FEATURE {i} FOR {self.agent_rep.name}" + state_input_port_name = f"FEATURE {i} FOR {self.agent_rep.name}" # SPEC # Pare and assign specs for INPUT Nodes already in agent_rep (i.e., unassigned above) @@ -2142,14 +2205,14 @@ def _parse_specs(state_feature_specs, spec_type="list"): state_feature_fct = spec[1] spec = spec[0] if is_numeric(spec): - spec_name = f"{port_name} {DEFAULT_VARIABLE.upper()}" + state_input_port_name = f"{port_name} {DEFAULT_VARIABLE.upper()}" elif isinstance(spec, (Port, Mechanism, Composition)): if hasattr(spec, 'full_name'): - spec_name = spec.full_name + state_input_port_name = spec.full_name else: - spec_name = spec.name + state_input_port_name = spec.name elif isinstance(spec, dict): - spec_name = spec[NAME] if NAME in spec else f"STATE FEATURE INPUT for {port_name}" + state_input_port_name = spec[NAME] if NAME in spec else f"STATE FEATURE INPUT for {port_name}" # tuple specification of function (assigned above) overrides dictionary specification if state_feature_fct is None: if FUNCTION in spec: @@ -2157,6 +2220,7 @@ def _parse_specs(state_feature_specs, spec_type="list"): elif PARAMS in spec and FUNCTION in spec[PARAMS]: state_feature_fct = spec[PARAMS][FUNCTION] elif spec == SHADOW_INPUTS: + # Shadow the specified port spec = port elif spec is not None: assert False, f"PROGRAM ERROR: unrecognized form of state_feature specification for {self.name}" @@ -2168,10 +2232,12 @@ def _parse_specs(state_feature_specs, spec_type="list"): parsed_feature_specs.append(spec) self._state_feature_functions.append(state_feature_fct) self._specified_INPUT_Node_InputPorts_in_order.append(port) - spec_names.append(spec_name) + state_input_port_names.append(state_input_port_name) self.parameters.state_feature_specs.set(parsed_feature_specs, override=True) - return spec_names or [] + return state_input_port_names or [] + + # END OF PARSE SPECS ----------------------------------------------------------------------------------- user_specs = self.parameters.state_feature_specs.spec @@ -2182,7 +2248,9 @@ def _parse_specs(state_feature_specs, spec_type="list"): specs = [user_specs] * len(agent_rep_input_ports) # OK to assign here (rather than in _parse_secs()) since spec is intended for *all* state_input_ports self.parameters.state_feature_specs.set(specs, override=True) - input_port_names = _parse_specs(specs, 'list') + state_input_port_names = _parse_specs(state_feature_specs=specs, + specified_input_ports=agent_rep_input_ports, + spec_type='list') # LIST OR SHADOW_INPUTS DICT: source specs # Source specs but not INPUT Nodes specified; spec is either: @@ -2229,7 +2297,10 @@ def _parse_specs(state_feature_specs, spec_type="list"): specs = user_specs[SHADOW_INPUTS] spec_type = f"{SHADOW_INPUTS.upper()} dict" - input_port_names = _parse_specs(specs, spec_type) + specified_input_ports = agent_rep_input_ports + [None] * (len(specs) - len(agent_rep_input_ports)) + state_input_port_names = _parse_specs(state_feature_specs=specs, + specified_input_ports=specified_input_ports, + spec_type=spec_type) # FIX: 2/25/22 - ?ITEMS IN set ARE SHADOWED, BUT UNSPECIFIED ITEMS IN SET AND DICT ARE ASSIGNED DEFAULT VALUES # SET OR DICT: specification by INPUT Nodes @@ -2294,7 +2365,9 @@ def _parse_specs(state_feature_specs, spec_type="list"): specs = [expanded_dict_with_ports[port] if port is not None and port in all_specified_ports else self.state_feature_default for port in all_specified_ports] - input_port_names = _parse_specs(specs) + state_input_port_names = _parse_specs(state_feature_specs=specs, + specified_input_ports=list(all_specified_ports), + spec_type='dict') else: assert False, f"PROGRAM ERROR: Unanticipated type specified for '{STATE_FEATURES}' arg of '{self.name}: " \ @@ -2303,47 +2376,77 @@ def _parse_specs(state_feature_specs, spec_type="list"): # CONSTRUCT InputPort SPECS ----------------------------------------------------------------------------- state_input_port_specs = [] + for i in range(self._num_state_feature_specs): # Note: state_feature_specs have now been parsed (user's specs are in parameters.state_feature_specs.spec); # state_feature_specs correspond to all InputPorts of agent_rep's INPUT Nodes (including nested ones) spec = self.state_feature_specs[i] + if spec is None: continue - spec = _parse_shadow_inputs(self, spec) - # If spec is numeric, assign its `default_value ` attribute as DEFAULT_VARIABLE, - # and the spec's value to the VALUE entry, which will assign it as its default_variable attribute - if is_numeric(spec): - spec_val = copy.copy(spec) - spec = {VALUE: spec_val, - PARAMS: {DEFAULT_INPUT: DEFAULT_VARIABLE} - } - else: - spec = spec[0] # _parse_shadow_inputs(self, spec) returns a list, even when passed a single item - # If optimization uses Composition, assume that shadowing a Mechanism means shadowing its primary InputPort - if isinstance(spec, Mechanism): - if self.agent_rep_type == COMPOSITION: - # FIX: 11/29/21: MOVE THIS TO _parse_shadow_inputs - # (ADD ARG TO THAT FOR DOING SO, OR RESTRICT TO InputPorts IN GENERAL) - if len(spec.input_ports)!=1: - raise OptimizationControlMechanismError( - f"A Mechanism ({spec.name}) is specified to be shadowed in the '{STATE_FEATURES}' arg " - f"for '{self.name}', but it has more than one {InputPort.__name__}; a specific one of its " - f"{InputPort.__name__}s must be specified to be shadowed.") - spec = spec.input_port + + # FIX: 3/22/22 - COULD/SHOULD ANY OF THE FOLLOWING BE DONE IN _parse_specs(): + + if isinstance(self.state_feature_specs[i], dict): + # If spec is an InputPort specification dict: + # - if a Mechanism is specified as the source, specify for shadowing: + # - add SHADOW_INPUTS entry to specify shadowing of Mechanism's primary InputPort + # - process dict through _parse_shadow_inputs(), + # (preserves spec as dict in case other parameters (except function, handled below) are specified) + # - replace self.state_feature_specs[i] with the InputPort (required by state_feature_values) + # - if a function is specified, clear from dict + # - it has already been assigned to self._state_feature_functions in _parse_spec() + # - it will be properly assigned to InputPort specification dict in _assign_state_feature_function + def _validate_entries(spec=None, source=None): + if spec and source: + if (SHADOW_INPUTS in spec) or (PARAMS in spec and SHADOW_INPUTS in spec[PARAMS]): + error_msg = "both PROJECTIONS and SHADOW_INPUTS cannot specified" + elif len(convert_to_list(source)) != 1: + error_msg = "PROJECTIONS entry has more than one source" + return + else: + error_msg = f"missing a 'PROJECTIONS' entry specifying the source of the input." + raise OptimizationControlMechanismError(f"Error in InputPort specification dictionary used in '" + f"{STATE_FEATURES}' arg of '{self.name}': {error_msg}.") + # Handle shadowing of Mechanism as source + if PROJECTIONS in spec: + source = get_port_for_mech_spec(spec.pop(PROJECTIONS)) + if spec: # No need to check if nothing left in dict + _validate_entries(spec, source) + spec[SHADOW_INPUTS] = source + elif PARAMS in spec and PROJECTIONS in spec[PARAMS]: + source = get_port_for_mech_spec(spec[PARAMS].pop(PROJECTIONS)) + _validate_entries(spec, source) + spec[PARAMS][SHADOW_INPUTS] = source else: - spec = spec.output_port - # Update Mechanism spec with Port - self.state_feature_specs[i] = spec - if isinstance(spec, dict): - # Note: clear any functions specified; will be assigned in _assign_state_feature_function + _validate_entries() + + # Clear FUNCTION entry if self._state_feature_functions[i]: spec.pop(FUNCTION, None) if PARAMS in spec: spec[PARAMS].pop(FUNCTION, None) + + spec = _parse_shadow_inputs(self, spec)[0] # _parse_shadow_inputs returns a list, so get item + self.state_feature_specs[i] = source + + if is_numeric(spec): + # Construct InputPort specification dict that configures it to use its InputPort.default_variable + # as its input, and assigns the spec's value to the VALUE entry, + # which assigns it as the value of the InputPort.default_variable + spec_val = copy.copy(spec) + spec = {VALUE: spec_val, + PARAMS: {DEFAULT_INPUT: DEFAULT_VARIABLE}} + + if isinstance(spec, Mechanism): + # Replace with primary InputPort to be shadowed + spec = get_port_for_mech_spec(spec) + self.state_feature_specs[i] = spec + parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) if not parsed_spec[NAME]: - parsed_spec[NAME] = input_port_names[i] + parsed_spec[NAME] = state_input_port_names[i] if parsed_spec[PARAMS] and SHADOW_INPUTS in parsed_spec[PARAMS]: # Composition._update_shadow_projections will take care of PROJECTIONS specification @@ -2445,7 +2548,7 @@ def _update_state_features_dict(self): node = None if not (isinstance(node, str) and 'DEFERRED' in node): continue - if feature.owner not in self._get_agent_rep_input_receivers(comp_as_node=ALL): + if feature.owner not in self._get_agent_rep_input_receivers(): # Don't add to dict, will be dealt with or raise an error at run time continue self.state_feature_specs[i] = feature @@ -3244,75 +3347,89 @@ def num_state_input_ports(self): @property def state_features(self): - """Return {InputPort: source} for all INPUT Nodes of agent_rep and/or ones specified in state_feature_specs. + """Return {InputPort name: source name} for all state_features. If state_feature_spec is numeric for a Node, assign its value as the source - If existing INPUT Node is not specified in state_feature_specs, assign None as source + If existing INPUT Node is not specified in state_feature_specs, assign state_feature_default as source If an InputPort is referenced in state_feature_specs that is not yet in agent_rep, - assign "EXPECTED INPUT NODE n" as the entry for the key (where n is the sequential numbering of such refs); - it should be resolved by runtime, or an error is generated. + assign "DEFERRED INPUT NODE OF " as key for the entry; + (it should be resolved by runtime, or an error is generated). + If a state_feature_spec is referenced that is not yet in ocm.composition, + assign " NOT (YET) IN " as the value of the entry; + (it should be resolved by runtime, or an error is generated). """ - # FIX: 3/4/22 - REPLACE "EXPECTED" IN KEY WITH "DEFAULT VALUE FOR " - # for unspecified InputPorts if "needs_update_controller" is False - # - GET SOURCE OR SHADOWED SPEC + self._update_state_features_dict() agent_rep_input_ports = self.agent_rep.external_input_ports_of_all_input_nodes - sources = [np.array(s).tolist() if is_numeric(s) else s - for s in list(self._get_state_feature_sources().values())] - # FIX: USES SOURCES AS VALUES FOR DICT BELOW state_features_dict = {} - # Use num_state_feature_specs here instead of num_state_input_ports as there may be some "null" (None) specs - j = 0 + state_input_port_num = 0 + + # Process all state_feature_specs, that may include ones for INPUT Nodes not (yet) in agent_rep for i in range(self._num_state_feature_specs): spec = self.state_feature_specs[i] - # FIX: 3/20/22 - USE KEYS RETURNED FROM _get_state_feature_sources?? - # Assign InputPorts of INPUT Nodes of agent_rep as keys - if self._specified_INPUT_Node_InputPorts_in_order[i] in agent_rep_input_ports: - key = self._specified_INPUT_Node_InputPorts_in_order[i] + input_port = self._specified_INPUT_Node_InputPorts_in_order[i] + # Get key for state_features dict + if input_port in agent_rep_input_ports: + # Specified InputPort belongs to an INPUT Node already in agent_rep, so use as key + key = input_port.full_name else: - key = f"EXPECTED INPUT NODE {i} OF {self.agent_rep.name}" - if spec is not None: - state_features_dict[key] = sources[j] - j += 1 + # Specified InputPort is not (yet) in agent_rep + input_port_name = (f"{input_port.full_name} AS INPUT NODE" if input_port + else f"INPUT NODE {str(i-len(agent_rep_input_ports))}") + key = deferred_state_feature_node_msg(input_port_name, self.agent_rep.name) + + # Get source for state_features dict + if spec is None: + # no state_input_port has been constructed, so assign source as None + source = None else: - state_features_dict[key] = spec + if is_numeric(spec): + # assign numeric spec as source + source = spec + # increment state_port_num, since one is implemented for numeric spec + else: + # spec is a Component, so get distal source of Projection to state_input_port + # (spec if it is an OutputPort; or ??input_CIM.output_port if it spec for shadowing an input?? + state_input_port = self.state_input_ports[state_input_port_num] + if self.composition._is_in_composition(spec): + source = spec.full_name + else: + source = deferred_state_feature_spec_msg(spec.full_name, self.composition.name) + state_input_port_num += 1 + + state_features_dict[key] = source return state_features_dict - # FIX: 3/20/22 - RESTORE THIS AS PROPERTY ONCE source_and_destinations IS EITHER REMOVED OR REFACTORED SIMILARLY - def _get_state_feature_sources(self): - """Dict with {InputPort: source} for all INPUT Nodes of agent_rep, and sources in **state_feature_specs.""" - source_dict = {} - specified_state_features = [spec for spec in self.state_feature_specs if spec is not None] - missing_port_index = 0 - for state_index, port in enumerate(self.state_input_ports): - if not port.path_afferents: - if port.default_input is DEFAULT_VARIABLE: - if specified_state_features[state_index] is not None: - source = specified_state_features[state_index] - else: - source = DEFAULT_VARIABLE - else: - source = specified_state_features[state_index] - input_node = f"EXPECTED INPUT NODE {missing_port_index} OF {self.agent_rep.name}" - missing_port_index += 1 - else: - get_info_method = self.composition._get_source - # FIX: 1/8/22: ASSUMES ONLY ONE PROJECTION PER STATE FEATURE - if port.shadow_inputs: - port = port.shadow_inputs - if port.owner in self.composition.nodes: - composition = self.composition - else: - composition = port.path_afferents[0].sender.owner.composition - get_info_method = composition._get_destination - source, _, comp = get_info_method(port.path_afferents[0]) - input_node = self._specified_INPUT_Node_InputPorts_in_order[state_index] - source_dict.update({input_node: source}) - return source_dict + @property + def state(self): + """Array that is concatenation of state_feature_values and control_allocations""" + # Use self.state_feature_values Parameter if state_features specified; else use state_input_port values + return list(self.state_feature_values.values()) + list(self.control_allocation) + + @property + def state_distal_sources_and_destinations_dict(self): + """Return dict with (Port, Node, Composition, index) tuples as keys and corresponding state[index] as values. + Initial entries are for sources of the state_feature_values (i.e., distal afferents for state_input_ports) + and subsequent entries are for destination parameters modulated by the OptimizationControlMechanism's + ControlSignals (i.e., distal efferents of its ControlProjections). + Note: the index is required, since a state_input_port may have more than one afferent Projection + (that is, a state_feature_value may be determined by Projections from more than one source), + and a ControlSignal may have more than one ControlProjection (that is, a given element of the + control_allocation may apply to more than one Parameter). However, for state_input_ports that shadow + a Node[InputPort], only that Node[InputPort] is listed in state_dict even if the Node[InputPort] being + shadowed has more than one afferent Projection (this is because it is the value of the Node[InputPort] + (after it has processed the value of its afferent Projections) that determines the input to the + state_input_port. + """ + sources_and_destinations = self.state_feature_sources + sources_and_destinations.update(self.control_signal_destinations) + return sources_and_destinations @property def state_feature_sources(self): - """Dict with {InputPort: source} for all INPUT Nodes of agent_rep, and sources in **state_feature_specs.""" + """Dict with {InputPort: source} for all INPUT Nodes of agent_rep, and sources in **state_feature_specs. + Used by state_distal_sources_and_destinations_dict() + """ state_dict = {} # FIX: 3/4/22 - THIS NEEDS TO HANDLE BOTH state_input_ports BUT ALSO state_feature_values FOR WHICH THERE ARE NO INPUTPORTS specified_state_features = [spec for spec in self.state_feature_specs if spec is not None] @@ -3351,31 +3468,6 @@ def control_signal_destinations(self): state_dict.update({(port, node, comp, state_index + ctl_index):self.state[state_index + ctl_index]}) return state_dict - @property - def state(self): - """Array that is concatenation of state_feature_values and control_allocations""" - # Use self.state_feature_values Parameter if state_features specified; else use state_input_port values - return list(self.state_feature_values.values()) + list(self.control_allocation) - - @property - def state_distal_sources_and_destinations_dict(self): - """Return dict with (Port, Node, Composition, index) tuples as keys and corresponding state[index] as values. - Initial entries are for sources of the state_feature_values (i.e., distal afferents for state_input_ports) - and subsequent entries are for destination parameters modulated by the OptimizationControlMechanism's - ControlSignals (i.e., distal efferents of its ControlProjections). - Note: the index is required, since a state_input_port may have more than one afferent Projection - (that is, a state_feature_value may be determined by Projections from more than one source), - and a ControlSignal may have more than one ControlProjection (that is, a given element of the - control_allocation may apply to more than one Parameter). However, for state_input_ports that shadow - a Node[InputPort], only that Node[InputPort] is listed in state_dict even if the Node[InputPort] being - shadowed has more than one afferent Projection (this is because it is the value of the Node[InputPort] - (after it has processed the value of its afferent Projections) that determines the input to the - state_input_port. - """ - sources_and_destinations = self.state_feature_sources - sources_and_destinations.update(self.control_signal_destinations) - return sources_and_destinations - @property def _model_spec_parameter_blacklist(self): # default_variable is hidden in constructor arguments, diff --git a/psyneulink/core/components/ports/inputport.py b/psyneulink/core/components/ports/inputport.py index ccd7a618246..2b1ee1b637a 100644 --- a/psyneulink/core/components/ports/inputport.py +++ b/psyneulink/core/components/ports/inputport.py @@ -1566,7 +1566,7 @@ def _instantiate_input_ports(owner, input_ports=None, reference_value=None, cont return port_list def _parse_shadow_inputs(owner, input_ports): - """Parse any {SHADOW_INPUTS:[InputPort or Mechanism,...]} items in input_ports into InputPort specif. dict. + """Parse any {SHADOW_INPUTS:[InputPort or Mechanism,...]} items in input_ports into InputPort specification dict Return InputPort specification dict for any shadowing InputPorts, and unmodified spec for any others. """ diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 2b44976f831..1be8e26b4ce 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -4460,7 +4460,7 @@ def _handle_allow_probes_for_control(self, node): def _get_nested_compositions(self, nested_compositions=NotImplemented, visited_compositions=NotImplemented): - """Recursive search for and return all nested compositions. + """Recursively search for and return all nested compositions. :return @@ -4486,6 +4486,18 @@ def _get_all_nodes(self): """ return [k[0] for k in self._get_nested_nodes()] + list(self.nodes) + def _is_in_composition(self, component, nested=True): + """Return True if component is in Composition, including any nested Compositions if **nested** is True + Include input_CIM and output_CIM for self and all nested Compositions + """ + if isinstance(component, Port): + component = component.owner + + if component in self._all_nodes: + return True + if nested: + return any(component in comp._all_nodes for comp in self._get_nested_compositions()) + def _determine_origin_and_terminal_nodes_from_consideration_queue(self): """Assigns NodeRole.ORIGIN to all nodes in the first entry of the consideration queue and NodeRole.TERMINAL to all nodes in the last entry of the consideration queue. The ObjectiveMechanism of a Composition's @@ -8929,10 +8941,10 @@ def _instantiate_input_dict(self, inputs): Input dict, with added entries for any input Nodes or Ports for which input was not provided """ - # # FIX: 3/12/22 - NEED TO PREPROCESS PORTS FOR EACH NODE TO DETERMINE MAXIMUM NUMBER OF TRIALS FOR EACH, + # # FIX: 3/12/22 - DOCUMENT PREPROCESSING OF PORTS FOR EACH NODE TO DETERMINE MAXIMUM NUMBER OF TRIALS FOR EACH, # # TO SET mech_shape FOR BELOW, AND ASSIGN FILLERS FOR ONES THAT ARE SHORT OF THE MAX LENGTH - # # SHOULD ALSO ENFORCE 1 OR SAME NUMBER FOR ALL PORTS, AND FILL IN FOR ONES THAT ARE SHORT OF MAX - # # (NO NEED TO DO SO FOR MECH OR COMP SPECS SINCE THOSE ARE TESTED IN _validate_input_shapes + # # SHOULD ALSO ENFORCE 1 OR SAME NUMBER FOR ALL PORTS, AND FILL IN FOR ONES THAT ARE SHORT OF + # # MAX (NO NEED TO DO SO FOR MECH OR COMP SPECS SINCE THOSE ARE TESTED IN _validate_input_shapes # # ALSO NO NEED TO WORRY ABOUT DIFFERENCES ACROSS NODES, AS THAT TOO WILL BE TESTED THERE # Validate that keys for inputs are all legal entries diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 1f0153dc6e7..d201079f20f 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -4709,6 +4709,7 @@ def test_three_level_deep_modulation_routing_two_mech(self): result = c1.run({c2: [[2], [2]], ctrl1: [5]}) assert result == [10, 10] + @pytest.mark.state_features def test_four_level_nested_transfer_mechanism_composition_parallel(self): # mechanisms A = ProcessingMechanism(name="A", @@ -4730,6 +4731,7 @@ def test_four_level_nested_transfer_mechanism_composition_parallel(self): ret = comp_lvl0.run(inputs={comp_lvl1: {comp_lvl2: {comp_lvl3a: [[1.0]], comp_lvl3b: [[1.0]]}}}) assert np.allclose(ret, [[[0.52497918747894]], [[0.52497918747894]]]) + @pytest.mark.state_features @pytest.mark.control def test_four_level_nested_OCM_control(self): p_lvl3 = ProcessingMechanism(name='p_lvl3') @@ -4752,6 +4754,7 @@ def test_four_level_nested_OCM_control(self): result = c_lvl0.run([5]) assert result == [150] + @pytest.mark.state_features @pytest.mark.control def test_four_level_nested_dual_OCM_control(self): p_lvl3 = ProcessingMechanism(name='p_lvl3') @@ -4783,7 +4786,6 @@ def test_four_level_nested_dual_OCM_control(self): intensity_cost_function=lambda _: 0, modulates=(SLOPE, p_lvl3), allocation_samples=[10, 20, 30]))) - result = c_lvl0.run([5]) assert result == [4500] diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 3a01aac00d1..9456c7e2b5d 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -143,10 +143,7 @@ def test_objective_mechanism_spec_as_monitor_for_control_error(self): @pytest.mark.state_features @pytest.mark.parametrize("control_spec", [CONTROL, PROJECTIONS]) - @pytest.mark.parametrize("state_features_arg", [ - 'list', - 'dict' - ]) + @pytest.mark.parametrize("state_features_arg", ['list','dict']) def test_deferred_init(self, control_spec, state_features_arg): # Test to insure controller works the same regardless of whether it is added to a composition before or after # the nodes it connects to @@ -214,8 +211,25 @@ def test_deferred_init(self, control_spec, state_features_arg): assert any(expected_warning in repr(w.message) for w in warning.list) assert comp._controller_initialization_status == pnl.ContextFlags.DEFERRED_INIT - assert comp.controller.state_features == {'EXPECTED INPUT NODE 0 OF evc': (reward.input_port), - 'EXPECTED INPUT NODE 1 OF evc': (Input.input_port)} + if state_features_arg == 'list': + assert comp.controller.state_features == {'DEFERRED INPUT NODE 0 OF evc': + 'reward[InputPort-0] NOT (YET) IN evc', + 'DEFERRED INPUT NODE 1 OF evc': + 'Input[InputPort-0] NOT (YET) IN evc'} + assert comp.controller.state_feature_values == {'DEFERRED 0 OF evc': 'reward[InputPort-0] NOT (YET) IN evc', + 'DEFERRED 1 OF evc': 'Input[InputPort-0] NOT (YET) IN evc'} + + elif state_features_arg == 'dict': + assert comp.controller.state_features == {'DEFERRED reward[InputPort-0] AS INPUT NODE OF evc': + 'reward[InputPort-0] NOT (YET) IN evc', + 'DEFERRED Input[InputPort-0] AS INPUT NODE OF evc': + 'Input[InputPort-0] NOT (YET) IN evc'} + assert comp.controller.state_feature_values == {'DEFERRED reward[InputPort-0] OF evc': + 'reward[InputPort-0] NOT (YET) IN evc', + 'DEFERRED Input[InputPort-0] OF evc': + 'Input[InputPort-0] NOT (YET) IN evc'} + else: + assert False, f"TEST ERROR: unrecognized option '{state_features_arg}'" comp.add_node(reward, required_roles=[pnl.NodeRole.OUTPUT]) comp.add_node(Decision, required_roles=[pnl.NodeRole.OUTPUT]) @@ -223,8 +237,10 @@ def test_deferred_init(self, control_spec, state_features_arg): comp.add_linear_processing_pathway(task_execution_pathway) comp.enable_controller = True - assert comp.controller.state_features == {reward.input_port: reward.input_port, - Input.input_port: Input.input_port} + assert comp.controller.state_features == {'reward[InputPort-0]': 'reward[InputPort-0]', + 'Input[InputPort-0]': 'Input[InputPort-0]'} + assert comp.controller.state_feature_values == {reward.input_port: [0.], + Input.input_port: [0.]} # comp._analyze_graph() stim_list_dict = { @@ -285,12 +301,7 @@ def test_deferred_init(self, control_spec, state_features_arg): err_msg='Failed on expected_output[{0}]'.format(trial)) @pytest.mark.state_features - @pytest.mark.parametrize('state_features_option', [ - 'list', - 'set', - 'dict', - 'shadow_inputs_dict' - ]) + @pytest.mark.parametrize('state_features_option', ['list','set','dict','shadow_inputs_dict']) def test_partial_deferred_init(self, state_features_option): initial_node_a = pnl.TransferMechanism(name='ia') initial_node_b = pnl.ProcessingMechanism(name='ib') @@ -338,8 +349,27 @@ def test_partial_deferred_init(self, state_features_option): deferred_node_control_signal ]) ) - assert ocomp.controller.state_features == {initial_node_a.input_port: initial_node_a.input_port, - 'EXPECTED INPUT NODE 1 OF ocomp':deferred_node.input_port} + if state_features_option in {'list', 'shadow_inputs_dict'}: + assert ocomp.controller.state_features == {'ia[InputPort-0]': + 'ia[InputPort-0]', + 'DEFERRED INPUT NODE 0 OF ocomp': + 'deferred[InputPort-0] NOT (YET) IN ocomp'} + assert ocomp.controller.state_feature_values == {initial_node_a.input_port: [0.], + 'DEFERRED 0 OF ocomp': + 'deferred[InputPort-0] NOT (YET) IN ocomp'} + + elif state_features_option in {'dict', 'set'}: + assert ocomp.controller.state_features == {'ia[InputPort-0]': + 'ia[InputPort-0]', + 'DEFERRED deferred[InputPort-0] AS INPUT NODE OF ocomp': + 'deferred[InputPort-0] NOT (YET) IN ocomp'} + assert ocomp.controller.state_feature_values == {initial_node_a.input_port: [0.], + 'DEFERRED deferred[InputPort-0] OF ocomp': + 'deferred[InputPort-0] NOT (YET) IN ocomp'} + + else: + assert False, f"TEST ERROR: unrecognized option '{state_features_option}'" + if state_features_option in {'list', 'shadow_inputs_dict'}: # expected_text = 'The number of \'state_features\' specified for Controller (2) is more than the ' \ @@ -359,9 +389,11 @@ def test_partial_deferred_init(self, state_features_option): assert expected_text in error_text.value.error_value ocomp.add_linear_processing_pathway([deferred_node, initial_node_b]) - assert ocomp.controller.state_features == {initial_node_a.input_port: initial_node_a.input_port, - deferred_node.input_port: deferred_node.input_port} + assert ocomp.controller.state_features == {'ia[InputPort-0]': 'ia[InputPort-0]', + 'deferred[InputPort-0]': 'deferred[InputPort-0]'} + assert ocomp.controller.state_feature_values == {initial_node_a.input_port: [0.], + deferred_node.input_port: [0.]} result = ocomp.run({ initial_node_a: [1], deferred_node: [1] @@ -1004,9 +1036,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg if test_condition == 'single_none_spec': assert len(ocm.state_input_ports) == 0 - assert ocm.state_features == {ia.input_port: None, - oa.input_port: None, - ob.input_port: None} + assert ocm.state_features == {'IA[InputPort-0]': None, + 'OA[InputPort-0]': None, + 'OB[InputPort-0]': None} assert ocm.state_feature_values == {} if test_condition == 'single_shadow_spec': @@ -1014,9 +1046,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: oa.input_port, - ob.input_port: ob.input_port} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': 'OA[InputPort-0]', + 'OB[InputPort-0]': 'OB[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {ia.input_port: [0.], oa.input_port: [0.], ob.input_port: [0., 0., 0.]} @@ -1025,9 +1057,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: oa.input_port, - ob.input_port: ob.input_port} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': 'OA[InputPort-0]', + 'OB[InputPort-0]': 'OB[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {ia.input_port: [0.5], oa.input_port: [0.5], ob.input_port: [0.5, 0.5, 0.5]} @@ -1038,17 +1070,20 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'OA[OutputPort-0]', 'OB[InputPort-0] DEFAULT_VARIABLE'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: oa.output_port, - ob.input_port: [3, 1, 2]} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': 'OA[OutputPort-0]', + 'OB[InputPort-0]': [3, 1, 2]} + assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {ia.input_port: [0.0], + oa.input_port: [0.0], + ob.input_port: [3.0, 1.0, 2.0]} if test_condition == 'list_spec_with_none': assert len(ocm.state_input_ports) == 2 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'OB[InputPort-0] DEFAULT_VARIABLE'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: None, - ob.input_port: [3, 1, 2]} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': None, + 'OB[InputPort-0]': [3, 1, 2]} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [3, 1, 2]])) @@ -1059,9 +1094,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'Shadowed input of OC[InputPort-0]', 'OB[OutputPort-0]'] # 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: oc.input_port, - ob.input_port: ob.output_port} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': 'OC[InputPort-0]', + 'OB[InputPort-0]': 'OB[OutputPort-0]'} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [0.], [0, 0, 0]])) @@ -1071,9 +1106,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OC[InputPort-0]', 'OB[OutputPort-0]'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: oc.input_port, - ob.input_port: ob.output_port} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': 'OC[InputPort-0]', + 'OB[InputPort-0]': 'OB[OutputPort-0]'} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [0.], [0, 0, 0]])) @@ -1082,9 +1117,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert len(ocm.state_input_ports) == 1 assert ocm.state_input_ports.names == ['Shadowed input of OA[InputPort-0]'] # 'set_spec': {ob, icomp, oa}, # Note: out of order is OK - assert ocm.state_features == {ia.input_port: None, - oa.input_port: oa.input_port, - ob.input_port: None} + assert ocm.state_features == {'IA[InputPort-0]': None, + 'OA[InputPort-0]': 'OA[InputPort-0]', + 'OB[InputPort-0]': None} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [0.], [0, 0, 0]])) @@ -1094,9 +1129,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: oa.input_port, - ob.input_port: ob.input_port} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': 'OA[InputPort-0]', + 'OB[InputPort-0]': 'OB[InputPort-0]'} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [0.], [0, 0, 0]])) @@ -1106,9 +1141,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: oa.input_port, - ob.input_port: ob.input_port} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': 'OA[InputPort-0]', + 'OB[InputPort-0]': 'OB[InputPort-0]'} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [0.], [0, 0, 0]])) @@ -1118,9 +1153,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: oa.input_port, - ob.input_port: ob.input_port} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': 'OA[InputPort-0]', + 'OB[InputPort-0]': 'OB[InputPort-0]'} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [0.], [0, 0, 0]])) @@ -1129,9 +1164,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg assert len(ocm.state_input_ports) == 2 assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {ia.input_port: ia.input_port, - oa.input_port: None, - ob.input_port: ob.input_port} + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': None, + 'OB[InputPort-0]': 'OB[InputPort-0]'} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), [[0.], [0.], [0, 0, 0]])) @@ -1154,9 +1189,13 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'Shadowed input of OA[InputPort-0]', 'Shadowed input of OB[InputPort-0]'] # Note: oa is assigned to icomp due to ordering: - assert ocm.state_features == {ia.input_port: oa.output_port, - oa.input_port: oa.input_port, - ob.input_port: ob.input_port} + assert ocm.state_features == {'IA[InputPort-0]': 'OA[OutputPort-0]', + 'OA[InputPort-0]': 'OA[InputPort-0]', + 'OB[InputPort-0]': 'OB[InputPort-0]'} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) + assert error_or_warning_message in [warning[i].message.args[0] for i in range(len(warning))] else: @@ -1170,9 +1209,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'single_tuple_numeric_spec', # <- same value and function assigned to all INPUT Node InputPorts 'single_port_spec', # <- same Port for all INPUT Node InputPorts 'single_mech_spec', # <- same Mech's InputPort for INPUT Node InputPorts + 'nested_partial_list', # <- specify 1st 3 INPUT Node InputPorts; 4th (I2) should get shaddowed 'nested_partial_set', # <- only one of two INPUT Nodes of nested Comp in set format 'nested_partial_dict', # <- only one of two INPUT Nodes of nested Comp in dict format - 'nested_partial_list', # <- specify 1st 3 INPUT Node InputPorts; 4th (I2) should get shaddowed 'nested_full_set', # <- both of two INPUT Nodes of nested Comp in set format 'nested_full_dict', # <- both of two INPUT Nodes of nested Comp in dict format 'nested_comp_set', # <- nested Comp as itself in set format @@ -1183,8 +1222,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg @pytest.mark.state_features @pytest.mark.control @pytest.mark.composition - @pytest.mark.parametrize('nested_agent_rep', - [(False, 'OUTER COMP'),(True, 'MIDDLE COMP')], + @pytest.mark.parametrize('nested_agent_rep',[(False, 'OUTER COMP'),(True, 'MIDDLE COMP')], ids=['unnested','nested']) @pytest.mark.parametrize('state_features_arg', state_features_arg, ids= [f"state_feature-{x}" for x in state_features_arg] @@ -1228,7 +1266,7 @@ def test_state_features_in_nested_composition_as_agent_rep(self, nested_agent_re 'nested_full_dict': {A:A.input_port, I1:I2.input_port, I2:I1.input_port}, 'nested_comp_set': {mcomp}, 'nested_comp_dict': {mcomp: I1}, - 'no_spec': 'SHOULD ASSIGN NONE TO ALL INPUT Node InputPorts', + 'no_spec': 'SHOULD SHADOW INPUT Node InputPorts', 'bad': [D.output_port] }[state_features_arg] @@ -1265,107 +1303,100 @@ def test_state_features_in_nested_composition_as_agent_rep(self, nested_agent_re ocomp.run() if state_features_arg == 'single_numeric_spec': - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: [3], - A.input_ports[pnl.TARGET]: [3], - I1.input_port: [3], - I2.input_port: [3]} + assert ocm.state_features == {'A[SAMPLE]': [3], + 'A[TARGET]': [3], + 'I1[InputPort-0]': [3], + 'I2[InputPort-0]': [3]} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [3], A.input_ports[pnl.TARGET]: [3], I1.input_port: [3], I2.input_port: [3]} - elif state_features_arg == 'single_tuple_numeric_spec': - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: [3], - A.input_ports[pnl.TARGET]: [3], - I1.input_port: [3], - I2.input_port: [3]} + assert ocm.state_features == {'A[SAMPLE]': [3], + 'A[TARGET]': [3], + 'I1[InputPort-0]': [3], + 'I2[InputPort-0]': [3]} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [15], A.input_ports[pnl.TARGET]: [15], I1.input_port: [15], I2.input_port: [15]} - elif state_features_arg in {'single_port_spec'}: - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: I1.output_port, - A.input_ports[pnl.TARGET]: I1.output_port, - I1.input_port: I1.output_port, - I2.input_port: I1.output_port} + assert ocm.state_features == {'A[SAMPLE]': 'I1[OutputPort-0]', + 'A[TARGET]': 'I1[OutputPort-0]', + 'I1[InputPort-0]': 'I1[OutputPort-0]', + 'I2[InputPort-0]': 'I1[OutputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], A.input_ports[pnl.TARGET]: [0], I1.input_port: [0], I2.input_port: [0]} - elif state_features_arg in {'single_mech_spec'}: - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: I1.input_port, - A.input_ports[pnl.TARGET]: I1.input_port, - I1.input_port: I1.input_port, - I2.input_port: I1.input_port} + assert ocm.state_features == {'A[SAMPLE]': 'I1[InputPort-0]', + 'A[TARGET]': 'I1[InputPort-0]', + 'I1[InputPort-0]': 'I1[InputPort-0]', + 'I2[InputPort-0]': 'I1[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], A.input_ports[pnl.TARGET]: [0], I1.input_port: [0], I2.input_port: [0]} - elif state_features_arg in 'nested_partial_list': - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: I1.output_port, - A.input_ports[pnl.TARGET]: [2], - I1.input_port: I2.input_port, - I2.input_port: I2.input_port} + assert ocm.state_features == {'A[SAMPLE]': 'I1[OutputPort-0]', + 'A[TARGET]': [2], + 'I1[InputPort-0]': 'I2[InputPort-0]', + 'I2[InputPort-0]': 'I2[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], A.input_ports[pnl.TARGET]: [2], I1.input_port: [0], I2.input_port: [0]} - elif state_features_arg in 'nested_partial_set': - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: A.input_ports[pnl.SAMPLE], - A.input_ports[pnl.TARGET]: None, - I1.input_port: None, - I2.input_port: I2.input_port} + assert ocm.state_features == {'A[SAMPLE]': 'A[SAMPLE]', + 'A[TARGET]': None, + 'I1[InputPort-0]': None, + 'I2[InputPort-0]': 'I2[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], I2.input_port: [0]} elif state_features_arg == 'nested_partial_dict': - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: [3.5], - A.input_ports[pnl.TARGET]: A.input_ports[pnl.TARGET], - I1.input_port: I1.input_port, - I2.input_port: I1.input_port} + assert ocm.state_features == {'A[SAMPLE]': [3.5], + 'A[TARGET]': 'A[TARGET]', + 'I1[InputPort-0]': 'I1[InputPort-0]', + 'I2[InputPort-0]': 'I1[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [3.5], A.input_ports[pnl.TARGET]: [0], I1.input_port: [0], I2.input_port: [0]} - - elif state_features_arg == 'nested_full_set': - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: A.input_ports[pnl.SAMPLE], - A.input_ports[pnl.TARGET]: A.input_ports[pnl.TARGET], - I1.input_port: I1.input_port, - I2.input_port: I2.input_port} + elif state_features_arg in {'nested_full_set', 'nested_comp_set', 'no_spec'}: + assert ocm.state_features == {'A[SAMPLE]': 'A[SAMPLE]', + 'A[TARGET]': 'A[TARGET]', + 'I1[InputPort-0]': 'I1[InputPort-0]', + 'I2[InputPort-0]': 'I2[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], A.input_ports[pnl.TARGET]: [0], I1.input_port: [0], I2.input_port: [0]} - elif state_features_arg == 'nested_full_dict': - assert ocm.state_features == {A.input_port: A.input_port, - A.input_ports[pnl.TARGET]: A.input_port, - I1.input_port: I2.input_port, - I2.input_port: I1.input_port} + assert ocm.state_features == {'A[SAMPLE]': 'A[SAMPLE]', + 'A[TARGET]': 'A[SAMPLE]', + 'I1[InputPort-0]': 'I2[InputPort-0]', + 'I2[InputPort-0]': 'I1[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[0]: [0], A.input_ports[1]: [0], I1.input_port: [0], I2.input_port: [0]} - elif state_features_arg == 'nested_comp_dict': - assert ocm.state_features == {A.input_ports[pnl.SAMPLE]: I1.input_port, - A.input_ports[pnl.TARGET]: I1.input_port, - I1.input_port: I1.input_port, - I2.input_port: I1.input_port} + assert ocm.state_features == {'A[SAMPLE]': 'I1[InputPort-0]', + 'A[TARGET]': 'I1[InputPort-0]', + 'I1[InputPort-0]': 'I1[InputPort-0]', + 'I2[InputPort-0]': 'I1[InputPort-0]'} assert {k:v.tolist() for k,v in ocm.state_feature_values.items()} == {A.input_ports[pnl.SAMPLE]: [0], A.input_ports[pnl.TARGET]: [0], I1.input_port: [0], I2.input_port: [0]} + @pytest.mark.state_features @pytest.mark.control @pytest.mark.parametrize('state_fct_assignments', [ - # 'partial_w_dict', - # 'partial_w_params_dict', + 'partial_w_dict', + 'partial_w_params_dict', 'tuple_override_dict', 'tuple_override_params_dict', 'port_spec_dict_in_feat_dict', @@ -1373,6 +1404,8 @@ def test_state_features_in_nested_composition_as_agent_rep(self, nested_agent_re None ]) def test_state_feature_function_specs(self, state_fct_assignments): + """Test assignment of state_feature_functions in various configurations + Also test use of InputPort specification dictionary as state_feature_specification""" fct_a = pnl.AdaptiveIntegrator fct_b = pnl.Buffer(history=2) @@ -1383,7 +1416,7 @@ def test_state_feature_function_specs(self, state_fct_assignments): R = pnl.ProcessingMechanism(name='D') if state_fct_assignments == 'partial_w_dict': - state_features = [{pnl.PROJECTIONS: A, + state_features = [{pnl.PROJECTIONS: A, # Note: specification of A in dict is still interpreted as shadowing pnl.FUNCTION: fct_a}, (B, fct_b), C] @@ -1414,7 +1447,7 @@ def test_state_feature_function_specs(self, state_fct_assignments): C: C} state_feature_function = fct_c elif state_fct_assignments == 'all': - state_features = [(A.output_port, fct_a), (B, fct_b), (C, fct_c)] + state_features = [(A, fct_a), (B, fct_b), (C, fct_c)] state_feature_function = None else: state_features = [A, B, C] @@ -1437,7 +1470,7 @@ def test_state_feature_function_specs(self, state_fct_assignments): assert result == [[24.]] assert all(np.allclose(actual, expected) for actual, expected in zip(list(ocm.parameters.state_feature_values.get('test').values()), - [[20],[[1],[2]],[3]])) + [[2],[[1],[2]],[3]])) else: assert isinstance(ocm.state_input_ports[0].function, pnl.LinearCombination) assert isinstance(ocm.state_input_ports[1].function, pnl.LinearCombination) From f8130b494b5b35badb27e1e2c9267d796086c418 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Wed, 23 Mar 2022 15:55:57 -0400 Subject: [PATCH 254/285] Refactor/ocm/state features all as input ports (#2356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * • composition.py - add_projection: delete instantiation of shadow projections (handled by _update_shadow_projections) - remove calls to _update_shadows_dict * - * • test_two_origins_two_input_ports: crashes on failure of C->B to update * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * - * - * - * - * - * - * - * - * • inputport.py: - add figure for shadowing * - * - * • processingmechanism.py: - __init__(): modify typecheck for input_ports to allow single items * • optimizationcontrolmechanism.py - allow single spec (None, array, tuple, or Components) that is assigned to all INPUT Node InputPorts - state_feature_default is assigned to all unspecified INPUT Node InputPorts (for a list that is shorter than the number, a dict or a set that has fewer, or any that are added to agent_rep after controller is initially constructed and added to Composition) * - * - * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * - * - * - * - * - * • optimizationcontrolmechanism.py: - _state_feature_values_getter(): for numeric state_feature, return state_input_port.functio(numeric_value) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - added tests for single state_feature spec * - composition.py, inputport.py, optimizationcontrolmechanism.py: docstring mods re: "external InputPort" * - * - * - * • test_control.py: - test_state_features_in_nested_composition_as_agent_rep(): - add tests for single state_feature specs - add tests for INPUT Node with more than on InputPort * - * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * • optimizationcontrolmechanism.py: IN PROGRESS - _specified_INPUT_Node_InputPorts_in_order: use name (str) of items not yet in agent_rep rather than 'None' - state_features & state_feature_values: - use name (str) as key if INPUT Node InputPort is not yet in agent_rep - use name (str) if source (spec) is not yet in Composition * - * • optimizationcontrolmechanism.py: IMPLEMENTED - state_features: - convert all keys and values to port.full_name - if not yet in comp or agent_rep, add "DEFERRED..." * • optimizationcontrolmechanism.py: PASSES deferred_init and partial_deferred_init tests * • optimizationcontrolmechanism.py: - state_feature_values: strings for missing keys or values * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_ocm_state_feature_specs_and_warnings_and_errors * • test_control.py: PASSING all state_feature tests * • composition.py: add _is_in_composition method * - * - * - * - * - * - * - * - * - * • optimzationcontrolmechanism.py: fix figure * • optimzationcontrolmechanism.py: fix figure Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- docs/source/_static/Optimization_fig.svg | 1482 ++++++++++ docs/source/_static/Optimization_fig_BAK.svg | 2556 ------------------ 2 files changed, 1482 insertions(+), 2556 deletions(-) create mode 100644 docs/source/_static/Optimization_fig.svg delete mode 100644 docs/source/_static/Optimization_fig_BAK.svg diff --git a/docs/source/_static/Optimization_fig.svg b/docs/source/_static/Optimization_fig.svg new file mode 100644 index 00000000000..e69e444f6ba --- /dev/null +++ b/docs/source/_static/Optimization_fig.svg @@ -0,0 +1,1482 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OptimizationControlMechanism + + + + + + + + + + + + + + + + + + + + + + + Composition or + + + CompositionFunctionApproximator + + + + + + + + + + + + + + + + + + + + + + + OptimizationFunction + + + + + + + + + + + + + + + + + + + + + search_function + + + + + + + + + + + + + + + + + + + + + search_space + + + + + + + + + + + + + + + + + + + + + agent_rep + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (adapt) + + + + + + + + + call function + + + + pass argument + + + + + + Execution + + + + Update + + + feature values + + + + + + + + + + + + + + + + + + + + + + + + + + (Adapt) + + + + + + Optimize + + + control_allocation + + + Implement + + + control_allocation + + + + + + + + + + + + + objective_function + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return/assign value + + + + + + + + + + + + + + + + + evaluate + + + + + + + + + + + + + + + + + + + + + + + evaluate_agent_rep + + + + + + + + + + + + + + + + + + + + + outcome + + + + + + + + + + + State + + + + + + + + + + + + + + Objective + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + Composition Inputs + + + + + + + + + + + + + + + + + + + + + + + Monitored + + + + + + + + + + + + + + Values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net_outcome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + control_allocation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + state_features + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + Model-based + + + + + + + + + + + + + + + + + + + + Control + + + + + + + + + + + + Evaluation + + + + + + + + + + + + + + + + + + + + Processing + + + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + Objective + + + + + + Mechanism + + + + + + + + + + + + + + + + + “Model-free” + + + + Features + + + + Outcome + + + + + + + + + + + + + + + + + + + + + + + + + + + Optimization + + + Control + + + Mechanism + + + + + + + + + + + + Simulation + + + + + + + + + + + + + + + Objective + + + + + + Mechanism + + + + + + + + + + + + + agent_rep + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + agent_rep + + + + + + + Composition + + + Composition + + + + + + + + + + + + + + + Optimization + + + Control + + + Mechanism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Composition + + + + + + or + + + + + + Composition + + + + + + Function + + + + + + Approximator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Adaptation + + + + + + + + + +A +B + +Forms of Optimization +Control Flow + diff --git a/docs/source/_static/Optimization_fig_BAK.svg b/docs/source/_static/Optimization_fig_BAK.svg deleted file mode 100644 index 5358b4c7e01..00000000000 --- a/docs/source/_static/Optimization_fig_BAK.svg +++ /dev/null @@ -1,2556 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -A -Optimization - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Model-based - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cont - r - ol - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Evaluation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Objective - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - “Model-f - r - ee” - - - - - - - - - - - - - - - - - - - - - - - - - - - - Features - - - - - - - - - - - - - - - - - - - - - - - - - - - - Outcome - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Optimization - - - - - - - - - - - - - - - Cont - r - ol - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Simulation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Objective - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - agent_rep - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - agent_rep - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Composition - - - - - - - - - - - - - - - Composition - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Optimization - - - - - - - - - - - - - - - Cont - r - ol - - - - - - - - - - - - - - - Mechanism - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Composition - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Function - - - - - - - - - - - - - - - - - - - - - Approximator - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OptimizationControlMechanism - - - - Composition - - - - - OptimizationFunction - - - search_function - - - search_space - - - agent_rep - - - - call function - - pass argument - - - - Execution - - Update - - feature values - Optimize - - control_allocation - Implement - - control_allocation - - - objective_function - - - - return/assign value - - - - - - - evaluate - - - - - evaluate_agent_rep - State - - - - Objective - Mechanism - - - - - - - Composition Inputs - - - - - Monitored - - - - - - - - - - - - Values - - - - - - - - - net_outcome - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - state_features - - - costs - - - - - - - control_allocation - - - outcome - - - -B - - - From 138a291f149a5af277513297c8a2a0665b9f3e21 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Fri, 25 Mar 2022 10:18:54 -0400 Subject: [PATCH 255/285] Refactor/ocm/state features all as input ports (#2359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - * • composition.py - added property shadowing_dict that has shadowing ports as keys and the ports they shadow as values - refactored _update_shadowing_projections to use shadowing_dict * • optimizationcontrolmechanism.py - _update_state_input_ports: modified validations for nested nodes; still failing some tests * • optimizationcontrolmechanism.py - _update_state_input_ports: more careful and informative validation that state_input_ports are in comp or nested comp and are INPUT nodes thereof; passes all tests except test_two_origins_two_input_ports as before * • composition.py _get_invalid_aux_components(): defer all shadow projections until _update_shadow_projections * • composition.py _get_invalid_aux_components(): bug fix in test for shadow projections * Port: _remove_projection_to_port: don't reduce variable below length 1 even ports with no incoming projections have variable at least length 1 * • composition.py add_node(): marked (but haven't removed) code block instantiating shadow_projections that seems now to be redundant with _update_shadow_projection * • show_graph.py - _assign_cim_components: supress showing projections not in composition * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py - add show_projections_not_in_composition option for debugging * • composition.py _update_shadow_projections(): delete unused shadow projections and corresponding ports * • composition.py _update_shadow_projections(): fix bug in deletion of unused shadow projections and ports • test_show_graph: tests failing, need mods to accomodate changes * • composition.py: _analyze_graph(): add extra call to _determine_node_roles after _update_shadow_projections _run(): moved block of code at beginning initializing scheduler to after _complete_init_of_partially_initialized_nodes and _analyze_graph() • show_graph.py - add test to all loops on projections: "if proj in composition.projection" * • show_graph.py fixes; now passes all show_graph tests * - * • composition.py _update_shadow_projections: raise error for attempt to shadow INTERNAL Node of nested comp * - * - * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * - * - * - * - * - * - * - * - * • inputport.py: - add figure for shadowing * - * - * • processingmechanism.py: - __init__(): modify typecheck for input_ports to allow single items * • optimizationcontrolmechanism.py - allow single spec (None, array, tuple, or Components) that is assigned to all INPUT Node InputPorts - state_feature_default is assigned to all unspecified INPUT Node InputPorts (for a list that is shorter than the number, a dict or a set that has fewer, or any that are added to agent_rep after controller is initially constructed and added to Composition) * - * - * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * - * - * - * - * - * • optimizationcontrolmechanism.py: - _state_feature_values_getter(): for numeric state_feature, return state_input_port.functio(numeric_value) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - added tests for single state_feature spec * - composition.py, inputport.py, optimizationcontrolmechanism.py: docstring mods re: "external InputPort" * - * - * - * • test_control.py: - test_state_features_in_nested_composition_as_agent_rep(): - add tests for single state_feature specs - add tests for INPUT Node with more than on InputPort * - * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * • optimizationcontrolmechanism.py: IN PROGRESS - _specified_INPUT_Node_InputPorts_in_order: use name (str) of items not yet in agent_rep rather than 'None' - state_features & state_feature_values: - use name (str) as key if INPUT Node InputPort is not yet in agent_rep - use name (str) if source (spec) is not yet in Composition * - * • optimizationcontrolmechanism.py: IMPLEMENTED - state_features: - convert all keys and values to port.full_name - if not yet in comp or agent_rep, add "DEFERRED..." * • optimizationcontrolmechanism.py: PASSES deferred_init and partial_deferred_init tests * • optimizationcontrolmechanism.py: - state_feature_values: strings for missing keys or values * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_ocm_state_feature_specs_and_warnings_and_errors * • test_control.py: PASSING all state_feature tests * • composition.py: add _is_in_composition method * - * - * - * - * - * - * - * - * - * • optimzationcontrolmechanism.py: fix figure * • optimzationcontrolmechanism.py: fix figure * • optimizationcontrolmechanism.py: - fix bug in which state_feature_default = None was being ignored - fix bug in which deferred nodes were not being assigned state_feature_default * - * • optimizationcontrolmechanism.py: - fix bug in which state_feature_default = None was being ignored - fix bug in which deferred nodes were not being assigned state_feature_default * • test_control.py: - test_deferred_init, test_partial_deferred_init(): added tests for controller.state_input_ports path_afferents * - Co-authored-by: jdcpni Co-authored-by: Katherine Mantel --- .../control/optimizationcontrolmechanism.py | 202 +++++++++++++----- tests/composition/test_control.py | 83 +++---- tests/composition/test_show_graph.py | 16 +- 3 files changed, 202 insertions(+), 99 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 54f17e486b6..b1738d15e5c 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -131,20 +131,20 @@ `_ and `cognitive neuroscience `_ literatures, as described below. -.. figure:: _static/Optimization_fig.svg - :scale: 50% - :alt: OptimizationControlMechanism - - **Functional Anatomy of an OptimizationControlMechanism.** *Panel A:* Examples of use in fully model-based - and model-free optimization. Note that in the example of `model-based optimization - ` (left), the OptimizationControlMechanism uses the entire - `Composition` that it controls as its `agent_rep `, whereas in - the example of `model-free optimization ` (right) the - the `agent_rep ` is a `CompositionFunctionApproximator`. The `agent_rep - ` can also be another (presumably simpler) Composition that can be used - to implement forms of optimization intermediate between fully model-based and model-free. *Panel B:* Flow of - execution during optimization. In both panels, faded items show process of adaptation when using a - `CompositionFunctionApproximator` as the `agent_rep `. + .. figure:: _static/Optimization_fig.svg + :scale: 50% + :alt: OptimizationControlMechanism + + **Functional Anatomy of an OptimizationControlMechanism.** *Panel A:* Examples of use in fully model-based + and model-free optimization. Note that in the example of `model-based optimization + ` (left), the OptimizationControlMechanism uses the entire + `Composition` that it controls as its `agent_rep `, whereas in + the example of `model-free optimization ` (right) the + the `agent_rep ` is a `CompositionFunctionApproximator`. The `agent_rep + ` can also be another (presumably simpler) Composition that can be used + to implement forms of optimization intermediate between fully model-based and model-free. *Panel B:* Flow of + execution during optimization. In both panels, faded items show process of adaptation when using a + `CompositionFunctionApproximator` as the `agent_rep `. | .. _OptimizationControlMechanism_Model_Based: @@ -282,7 +282,7 @@ The **state_features** argument can be specified using any of the following formats: - .. _OptimizationControlMechanism_State_Feature_Input_Dict: + .. _OptimizationControlMechanism_State_Feature_Single_Spec: * *Single specification* -- any of the indivdiual specifications described `below ` can be directly to **state_features**, that is @@ -290,6 +290,8 @@ for each `external InputPort ` of the `agent_rep `. + .. _OptimizationControlMechanism_State_Feature_Input_Dict: + * *Inputs dictionary* -- specifies state_features (entry values) for individual `InputPorts ` and/or `INPUT ` `Nodes ` of the `agent_rep ` (entry keys). It must conform to the format used to `specify external inputs ` @@ -315,30 +317,38 @@ .. _OptimizationControlMechanism_State_Feature_List_Inputs: - * *List* -- a list of individual state_feature specifications, that can be any of the forms of individual input - specifications listed `below `. The items correspond - to all of the `external InputPorts ` of the `agent_rep - `, and must be specified in the order they are listed in the `agent_rep - `\\'s `external_input_ports_of_all_input_nodes + * *List* -- a list of individual state_feature specifications, that can be any of the forms of individual + input specifications listed `below `. The items + correspond to all of the `external InputPorts ` of the `agent_rep + `, and must be specified in the order they are listed in the + `agent_rep `\\'s `external_input_ports_of_all_input_nodes ` attribute. If the list is incomplete, the remaining - InputPorts are assigned `state_feature_default ` as their - state_feature specification. Items can be included in the list that have not yet been added to the - OptimizationControlMechanism's Composition or its `agent_rep `. However, - these must be added before the Composition is executed, and must appear in the list in the same position that the - InputPorts to which they pertain are listed in the `agent_rep `\\'s - `external_input_ports_of_all_input_nodes ` attribute, - once construction of the `agent_rep ` is complete. + InputPorts are assigned `state_feature_default ` + as their state_feature specification, which by default is *SHADOW_INPUTS* (see `below + `. Items can be included in the list that + have not yet been added to the OptimizationControlMechanism's Composition or its `agent_rep + `. However, these must be added before the Composition is executed, + and must appear in the list in the same position that the InputPorts to which they pertain are listed in + the `agent_rep `\\'s `external_input_ports_of_all_input_nodes + ` attribute, once construction of the `agent_rep + ` is complete. .. _OptimizationControlMechanism_State_Feature_Set_Inputs: * *Set* -- a set of `INPUT ` `Nodes ` of the `agent_rep - ` that are assigned *SHADOW_INPUTS* as their state_feature (see `below - <_OptimizationControlMechanism_SHADOW_INPUTS_State_Feature>`); that is, that should receive the same inputs - during evaluation as when the Composition of which the OptimizationControlMechanism is the `controller - ` is fully executed. The order of their specification does not matter; however, any of - the `agent_rep `\\'s `INPUT ` Nodes that are *not* included - in the set are assigned `state_feature_default ` as their - state_feature specification. + ` that are assigned *SHADOW_INPUTS* as their state_feature + -- that is, that should receive the same inputs during evaluation as when the Composition of which + the OptimizationControlMechanism is the `controller ` is fully executed + (see `below <_OptimizationControlMechanism_SHADOW_INPUTS_State_Feature>`). The order of their specification + does not matter; however, any of the `agent_rep `\\'s `INPUT + ` Nodes that are *not* included in the set are assigned `state_feature_default + ` as their state_feature specification. Note that, + since the default for `state_feature_default ` is + *SHADOW_INPUTS*, unless this is specified otherwise omitting items from a set has no effect (i.e., they + too are assigned *SHADOW_INPUTS*); for omitted items to be treated differently, `state_feature_default + ` must be specified; for example by assigning it + ``None`` so that items omitted from the set are assigned their default input value (see `below + `. .. _OptimizationControlMechanism_State_Feature_Individual_Specs: @@ -348,9 +358,9 @@ .. _OptimizationControlMechanism_None_State_Feature: - * *None* -- no `state_input_port ` is constructed for the - corresponding `INPUT ` `Node ` InputPort, and its the value of its `default - variable ` is used as the input to that InputPort whenever the + * *None* -- no `state_input_port ` is constructed for + the corresponding `INPUT ` `Node ` InputPort, and its the value + of its `default variable ` is used as the input to that InputPort whenever the ` is `evaluated `, irrespective of its input when the `agent_rep ` was last executed. @@ -1315,7 +1325,7 @@ class OptimizationControlMechanism(ControlMechanism): `OptimizationControlMechanism_State_Input_Ports` for additional details). state_feature_default : Mechanism, InputPort, OutputPort, Projection, dict, SHADOW_INPUTS, numeric value - determines the default used if the state_feature (i.e. source) is not otherwise specified for the `InputPort`of + determines the default used if the state_feature (i.e. source) is not otherwise specified for the `InputPort` of an `INPUT ` `Node ` of `agent_rep `. If it is None, then no corresponding `state_input_port ` is created for that InputPort, and its `default variable ` is used as its input when the @@ -1608,13 +1618,16 @@ class Parameters(ControlMechanism.Parameters): :type: state_feature_specs - see `state_feature_specs ` + This is for internal use only, including population of the state_features property + (see `state_features `) :default value: SHADOW_INPUTS :type: ``dict`` - state_feature_default - see `state_feature_default ` + state_feature_default_spec + This is a shell parameter to validate its assignment and explicity user specification of None + to override Parameter default; its .spec attribute is assigned to the user-facing + self.state_feature_default (see `state_feature_default `). :default value: SHADOW_INPUTS :type: @@ -1635,7 +1648,8 @@ class Parameters(ControlMechanism.Parameters): state_input_ports = Parameter(None, reference=True, stateful=False, loggable=False, read_only=True) state_feature_specs = Parameter(SHADOW_INPUTS, stateful=False, loggable=False, read_only=True, structural=True, parse_spec=True) - state_feature_default = Parameter(SHADOW_INPUTS, stateful=False, loggable=False, read_only=True) + state_feature_default_spec = Parameter(SHADOW_INPUTS, stateful=False, loggable=False, read_only=True, + structural=True) state_feature_function = Parameter(None, reference=True, stateful=False, loggable=False) state_feature_values = Parameter(None,getter=_state_feature_values_getter, user=False, pnl_internal=True, read_only=True) @@ -1665,9 +1679,9 @@ class Parameters(ControlMechanism.Parameters): saved_samples = None saved_values = None - def _validate_state_feature_default(self, state_feature_default): + def _validate_state_feature_default_spec(self, state_feature_default): if not (isinstance(state_feature_default, (InputPort, OutputPort, Mechanism)) - or state_feature_default == SHADOW_INPUTS + or state_feature_default in {SHADOW_INPUTS} or is_numeric(state_feature_default) or state_feature_default is None): return f"must be an InputPort, OutputPort, Mechanism, Composition, SHADOW_INPUTS or a list or array " \ @@ -1677,8 +1691,11 @@ def _validate_state_feature_default(self, state_feature_default): @tc.typecheck def __init__(self, agent_rep=None, - state_features: tc.optional((tc.any(str, Iterable, InputPort, OutputPort, Mechanism)))=SHADOW_INPUTS, - state_feature_default=None, + state_features: tc.optional((tc.any(str, Iterable, InputPort, + OutputPort, Mechanism)))=SHADOW_INPUTS, + # state_feature_default=None, + state_feature_default: tc.optional((tc.any(str, Iterable, + InputPort, OutputPort,Mechanism)))=SHADOW_INPUTS, state_feature_function: tc.optional(tc.optional(tc.any(dict, is_function_type)))=None, function=None, num_estimates=None, @@ -1729,7 +1746,7 @@ def __init__(self, # Store args for deferred initialization self._store_deferred_init_args(**locals()) self._init_args['state_feature_specs'] = state_features - self._init_args['state_feature_default'] = state_feature_default + self._init_args['state_feature_default_spec'] = state_feature_default # Flag for deferred initialization self.initialization_status = ContextFlags.DEFERRED_INIT @@ -1739,9 +1756,11 @@ def __init__(self, else: assert False, f"PROGRAM ERROR: 'agent_rep' arg should have been specified " \ f"in internal call to constructor for {self.name}." + + # If agent_rep is a Composition, but there are more state_features than INPUT Nodes, + # defer initialization until they are added elif agent_rep.componentCategory=='Composition': from psyneulink.core.compositions.composition import NodeRole - # If there are more state_features than INPUT Nodes in agent_rep, defer initialization until they are added if (state_features and len(convert_to_list(state_features)) > len(agent_rep.get_nodes_by_role(NodeRole.INPUT))): # Temporarily name InputPort @@ -1749,7 +1768,7 @@ def __init__(self, # Store args for deferred initialization self._store_deferred_init_args(**locals()) self._init_args['state_feature_specs'] = state_features - + self._init_args['state_feature_default_spec'] = state_feature_default # Flag for deferred initialization self.initialization_status = ContextFlags.DEFERRED_INIT return @@ -1757,7 +1776,7 @@ def __init__(self, super().__init__( agent_rep=agent_rep, state_feature_specs=state_features, - state_feature_default=state_feature_default, + state_feature_default_spec=state_feature_default, state_feature_function=state_feature_function, function=function, num_estimates=num_estimates, @@ -1909,7 +1928,6 @@ def _validate_input_nodes(self, nodes, enforce=None): agent_rep_input_ports = self._get_agent_rep_input_receivers(type=PORT) agent_rep_all_nodes = self.agent_rep._get_all_nodes() non_input_node_specs = [node for node in nodes - # if node not in self._get_agent_rep_input_receivers(type=NODE, comp_as_node=ALL)] if ((isinstance(node, (Mechanism, Composition)) and node not in agent_rep_input_nodes) or (isinstance(node, Port) and (not isinstance(node, InputPort) or node not in agent_rep_input_ports)))] @@ -2234,12 +2252,15 @@ def _parse_specs(state_feature_specs, specified_input_ports=None, spec_type="lis self._specified_INPUT_Node_InputPorts_in_order.append(port) state_input_port_names.append(state_input_port_name) + if not any(self._state_feature_functions): + self._state_feature_functions = None self.parameters.state_feature_specs.set(parsed_feature_specs, override=True) return state_input_port_names or [] # END OF PARSE SPECS ----------------------------------------------------------------------------------- user_specs = self.parameters.state_feature_specs.spec + self.state_feature_default = self.parameters.state_feature_default_spec.spec # SINGLE ITEM spec, SO APPLY TO ALL agent_rep_input_ports if (user_specs is None @@ -2351,7 +2372,12 @@ def _parse_specs(state_feature_specs, specified_input_ports=None, spec_type="lis expanded_dict_with_ports.update({port:user_specs[spec] for port in ports}) # # Get specified ports in order of agent_rep INPUT Nodes, with None assigned to any unspecified InputPorts - all_specified_ports = [port if port in expanded_specified_ports else None for port in agent_rep_input_ports] + all_specified_ports = [port if port in expanded_specified_ports + # MODIFIED 3/24/22 OLD: + else None for port in agent_rep_input_ports] + # MODIFIED 3/24/22 NEW: + # else self.state_feature_default for port in agent_rep_input_ports] + # MODIFIED 3/24/22 END # Get any not found anywhere (including nested) in agent_rep, which are placed at the end of list all_specified_ports.extend([port for port in expanded_specified_ports if port not in agent_rep_input_ports]) @@ -2477,7 +2503,12 @@ def _assign_state_feature_function(self, specification_dict, idx=None): assert len(self._state_feature_functions) == self._num_state_feature_specs, \ f"PROGRAM ERROR: Length of _state_feature_functions for {self.name} should be same " \ f"as number of state_input_port_dicts passed to _assign_state_feature_function" + # # MODIFIED 3/24/22 OLD: + # state_feature_functions = (self._state_feature_functions + # if self._state_feature_functions[0] is not None else None) + # MODIFIED 3/24/22 NEW: state_feature_functions = self._state_feature_functions + # MODIFIED 3/24/22 END except AttributeError: # state_features assigned automatically in _update_state_input_ports_for_controller, # so _state_feature_functions (for individual state_features) not created @@ -2520,6 +2551,69 @@ def _update_state_input_ports_for_controller(self, context=None): if self.agent_rep_type != COMPOSITION: return + from psyneulink.core.compositions.composition import Composition + num_agent_rep_input_ports = len(self.agent_rep_input_ports) + num_state_feature_specs = len(self.state_feature_specs) + # if self.num_state_input_ports != num_agent_rep_input_ports: + if num_state_feature_specs < num_agent_rep_input_ports: + # agent_rep is Composition, but state_input_ports are missing for some agent_rep INPUT Node InputPorts + # so construct a state_input_port for each missing one, using state_feature_default; + # note: assumes INPUT Nodes added are at the end of the list in self.agent_rep_input_ports + # FIX: 3/24/22 - HANDLED ELSEWHWERE WITH ERROR MESSAGE (SHOULD FIGURE OUT WHERE) + # if context.flags & ContextFlags.PREPARING: + # # At run time, insure that there not *more* state_input_ports than agent_rep INPUT Node InputPorts + # assert self.num_state_input_ports < len(self.agent_rep_input_ports), \ + # f"PROGRAM ERROR: More state_input_ports assigned to '{self.name}' ({self.num_state_input_ports}) " \ + # f"than agent_rep ('{self.agent_rep.name}') has INPUT Node InputPorts ({num_agent_rep_input_ports})." + # FIX: 3/24/22 - SHOULD PROBABLY REFACTOR THIS TO CALL _parse_state_feature_specs + state_input_ports = [] + local_context = Context(source=ContextFlags.METHOD) + default = self.state_feature_default + new_agent_rep_input_ports = self.agent_rep_input_ports[self.num_state_input_ports:] + for input_port in new_agent_rep_input_ports: + # Instantiate state_input_port for each agent_rep INPUT Node InputPort not already specified: + # OLD: [MODIFIED] + params = {INTERNAL_ONLY:True, + PARAMS: {}} + if default is None: + continue + if default == SHADOW_INPUTS: + params[SHADOW_INPUTS] = input_port + input_port_name = f"{SHADOW_INPUT_NAME}{input_port.full_name}]" + self.state_feature_specs.append(input_port) + elif is_numeric(default): + params[VALUE]: default + # FIX: 3/24/22 - NEED TO STANDARDIZE NAME FOR NUMERIC spec AND ALIGN WITH OTHER USES + input_port_name = f"NUMERIC INPUT FOR {input_port.full_name}]" + self.state_feature_specs.append(default) + elif isinstance(default, (Port, Mechanism, Composition)): + params[PROJECTIONS]: default + self.state_feature_specs.append(default) + if self.state_feature_function: + # Use **state_feature_function** if specified by user in constructor + params = self._assign_state_feature_function(params) + state_input_port = _instantiate_port(name=input_port_name, + port_type=InputPort, + owner=self, + reference_value=input_port.value, + params=params, + context=local_context) + + state_input_ports.append(state_input_port) + # FIX: 3/24/22 - MAKE THIS A PROPERTY? (OR NEED IT REMAIN STABLE FOR LOOPS?) + self._num_state_feature_specs += 1 + + self.add_ports(state_input_ports, + update_variable=False, + context=local_context) + + # Assign OptimizationControlMechanism attributes + self.state_input_ports.extend(state_input_ports) + # FIX: 3/24/22 - ?OK: + # self._num_state_feature_specs = len(self.state_input_ports) + self._specified_INPUT_Node_InputPorts_in_order = self.agent_rep_input_ports + # MODIFIED 3/24/22 END + if self.state_feature_specs: # Restrict validation and any further instantiation of state_input_ports # until run time, when the Composition is expected to be fully constructed @@ -3338,6 +3432,10 @@ def agent_rep_type(self): else: return None + @property + def agent_rep_input_ports(self): + return self._get_agent_rep_input_receivers(type=PORT) + @property def num_state_input_ports(self): try: diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 9456c7e2b5d..b58d8cae590 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -241,6 +241,7 @@ def test_deferred_init(self, control_spec, state_features_arg): 'Input[InputPort-0]': 'Input[InputPort-0]'} assert comp.controller.state_feature_values == {reward.input_port: [0.], Input.input_port: [0.]} + assert all(p.path_afferents for p in comp.controller.state_input_ports) # comp._analyze_graph() stim_list_dict = { @@ -394,6 +395,8 @@ def test_partial_deferred_init(self, state_features_option): assert ocomp.controller.state_feature_values == {initial_node_a.input_port: [0.], deferred_node.input_port: [0.]} + assert all(p.path_afferents for p in ocomp.controller.state_input_ports) + result = ocomp.run({ initial_node_a: [1], deferred_node: [1] @@ -837,7 +840,6 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c assert err.value.error_value == err_msg messages = [ - # 0 f"There are fewer '{pnl.STATE_FEATURES}' specified for 'OptimizationControlMechanism-0' than the number " f"of InputPort's for all of the INPUT Nodes of its agent_rep ('OUTER COMP'); the remaining inputs will be " @@ -913,42 +915,40 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c f"'Shadowed input of EXT[InputPort-0]-2']) " f"that are missing from 'OUTER COMP' and any Compositions nested within it." ] - state_feature_args = [ - ('single_none_spec', None, None), - ('single_shadow_spec', None, None), - ('single_tuple_shadow_spec', None, None), - ('partial_legal_list_spec', messages[0], UserWarning), - ('full_list_spec', None, None), - ('list_spec_with_none', None, None), - ('input_dict_spec', None, None), - ('input_dict_spec_short', None, None), - ('set_spec_short', None, None), - ('set_spec', None, None), - ('set_spec_port', None, None), - ('no_specs', None, None), - ('shadow_inputs_dict_spec', None, None), - ('shadow_inputs_dict_spec_w_none', None, None), - ('misplaced_shadow', messages[1], pnl.CompositionError), - ('ext_shadow', messages[2], pnl.OptimizationControlMechanismError), - ('ext_output_port', messages[3], pnl.OptimizationControlMechanismError), - ('input_format_wrong_shape', messages[4], pnl.OptimizationControlMechanismError), - ('too_many_inputs_warning', messages[5], UserWarning), - ('too_many_w_node_not_in_composition_warning', messages[6], UserWarning), - ('too_many_inputs_error', messages[7], pnl.OptimizationControlMechanismError), - ('bad_single_spec', messages[13], pnl.OptimizationControlMechanismError), - ('bad_dict_spec_warning', messages[8], UserWarning), - ('bad_dict_spec_error', messages[8], pnl.OptimizationControlMechanismError), - ('bad_shadow_inputs_dict_spec_error', messages[12], pnl.OptimizationControlMechanismError), - ('comp_in_list_spec', messages[10], pnl.OptimizationControlMechanismError), - ('comp_in_shadow_inupts_spec', messages[11], pnl.OptimizationControlMechanismError) + # STATE_FEATURE_ARGS, STATE_FEATURE_DEFAULT, ERROR_OR_WARNING_MSG, EXCEPTION_TYPE + ('single_none_spec', pnl.SHADOW_INPUTS, None, None), + ('single_shadow_spec', pnl.SHADOW_INPUTS, None, None), + ('single_tuple_shadow_spec', pnl.SHADOW_INPUTS, None, None), + ('partial_legal_list_spec', pnl.SHADOW_INPUTS, messages[0], UserWarning), + ('full_list_spec', pnl.SHADOW_INPUTS, None, None), + ('list_spec_with_none', pnl.SHADOW_INPUTS, None, None), + ('input_dict_spec', pnl.SHADOW_INPUTS, None, None), + ('input_dict_spec_short', pnl.SHADOW_INPUTS, None, None), + ('set_spec_short', None, None, None), + ('set_spec', pnl.SHADOW_INPUTS, None, None), + ('set_spec_port', pnl.SHADOW_INPUTS, None, None), + ('no_specs', None, None, None), + ('shadow_inputs_dict_spec', pnl.SHADOW_INPUTS, None, None), + ('shadow_inputs_dict_spec_w_none', pnl.SHADOW_INPUTS, None, None), + ('misplaced_shadow', pnl.SHADOW_INPUTS, messages[1], pnl.CompositionError), + ('ext_shadow', pnl.SHADOW_INPUTS, messages[2], pnl.OptimizationControlMechanismError), + ('ext_output_port', pnl.SHADOW_INPUTS, messages[3], pnl.OptimizationControlMechanismError), + ('input_format_wrong_shape', pnl.SHADOW_INPUTS, messages[4], pnl.OptimizationControlMechanismError), + ('too_many_inputs_warning', pnl.SHADOW_INPUTS, messages[5], UserWarning), + ('too_many_w_node_not_in_composition_warning', pnl.SHADOW_INPUTS, messages[6], UserWarning), + ('too_many_inputs_error', pnl.SHADOW_INPUTS, messages[7], pnl.OptimizationControlMechanismError), + ('bad_single_spec', pnl.SHADOW_INPUTS, messages[13], pnl.OptimizationControlMechanismError), + ('bad_dict_spec_warning', pnl.SHADOW_INPUTS, messages[8], UserWarning), + ('bad_dict_spec_error', pnl.SHADOW_INPUTS, messages[8], pnl.OptimizationControlMechanismError), + ('bad_shadow_inputs_dict_spec_error', pnl.SHADOW_INPUTS, messages[12], pnl.OptimizationControlMechanismError), + ('comp_in_list_spec', pnl.SHADOW_INPUTS, messages[10], pnl.OptimizationControlMechanismError), + ('comp_in_shadow_inupts_spec', pnl.SHADOW_INPUTS, messages[11], pnl.OptimizationControlMechanismError) ] - if len(state_feature_args) != 23: print("\n\n**********************************************************************************************") print("*** RESTORE state_feature_args IN test_ocm_state_feature_specs_and_warnings_and_errors() *****") print("***********************************************************************************************") - @pytest.mark.state_features @pytest.mark.control @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x[0] for x in state_feature_args]) @@ -957,8 +957,9 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg """See test_nested_composition_as_agent_rep() for additional tests of state_features specification.""" test_condition = state_feature_args[0] - error_or_warning_message = state_feature_args[1] - exception_type = state_feature_args[2] + state_feature_default = state_feature_args[1] + error_or_warning_message = state_feature_args[2] + exception_type = state_feature_args[3] ia = pnl.ProcessingMechanism(name='IA') ib = pnl.ProcessingMechanism(name='IB') @@ -977,7 +978,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg # Legal state_features specifications 'single_none_spec': None, 'single_shadow_spec': pnl.SHADOW_INPUTS, - 'single_tuple_shadow_spec': (pnl.SHADOW_INPUTS, pnl.Logistic), + 'single_tuple_shadow_spec': (pnl.SHADOW_INPUTS, pnl.Logistic), # state_feature_values should be 0.5 'partial_legal_list_spec': [oa.output_port], # only specifies ia; oa and ob assigned default inputs 'full_list_spec': [ia.input_port, oa.output_port, [3,1,2]], 'list_spec_with_none': [ia.input_port, None, [3,1,2]], @@ -1021,7 +1022,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg else: ocm = pnl.OptimizationControlMechanism(state_features=state_features, - state_feature_default=pnl.SHADOW_INPUTS, + state_feature_default=state_feature_default, objective_mechanism=objective_mechanism, monitor_for_control=monitor_for_control, function=pnl.GridSearch(), @@ -1210,15 +1211,19 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'single_port_spec', # <- same Port for all INPUT Node InputPorts 'single_mech_spec', # <- same Mech's InputPort for INPUT Node InputPorts 'nested_partial_list', # <- specify 1st 3 INPUT Node InputPorts; 4th (I2) should get shaddowed - 'nested_partial_set', # <- only one of two INPUT Nodes of nested Comp in set format - 'nested_partial_dict', # <- only one of two INPUT Nodes of nested Comp in dict format - 'nested_full_set', # <- both of two INPUT Nodes of nested Comp in set format - 'nested_full_dict', # <- both of two INPUT Nodes of nested Comp in dict format + 'nested_partial_set', # <- only 2 of 3 INPUT Nodes of nested Comp in set format; + 'nested_partial_dict', # <- only 2 of 3 INPUT Nodes of nested Comp in dict format + 'nested_full_set', # <- all 3 INPUT Nodes of nested Comp in set format + 'nested_full_dict', # <- both of 2 INPUT Nodes of nested Comp in dict format 'nested_comp_set', # <- nested Comp as itself in set format 'nested_comp_dict', # <- nested Comp as itself in dict format with a single spec for all INPUT Nodes 'no_spec', # <- Assign state_feature_default to all Nodes 'bad' # <- Mechanism not in agent_rep ] + if len(state_feature_args) != 13: + print("\n\n**********************************************************************************************") + print("*** RESTORE state_feature_args IN test_state_features_in_nested_composition_as_agent_rep() *****") + print("***********************************************************************************************") @pytest.mark.state_features @pytest.mark.control @pytest.mark.composition diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index e69dbc94407..828468b5151 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -383,35 +383,35 @@ def test_nested_learning(self, show_graph_kwargs, expected_output): _nested_learning_test_with_user_specified_target_in_outer_composition_data = [ ( {'show_nested': False, 'show_cim': False, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' ), ( {'show_nested': NESTED, 'show_cim': False, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> Target [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> Target [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" -> TARGET [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" -> TARGET [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' ), ( {'show_nested': NESTED, 'show_cim': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" -> TARGET [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> Target [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"INNER OUTPUT" -> "NESTED COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [color=green penwidth=3 rank=source shape=oval]\n\t"OUTER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\tINTERNAL [color=black penwidth=1 rank=same shape=oval]\n\t"OUTER INPUT" -> INTERNAL [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET -> "NESTED COMPOSITION INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM" -> "OUTER OUTPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"COMPOSITION INPUT_CIM" -> "OUTER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" -> TARGET [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"OUTER OUTPUT" -> "COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER -> INTERNAL [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [color=purple penwidth=1 rank=min shape=oval]\n\t"OBJECTIVE MECHANISM" -> CONTROLLER [label="" color=purple penwidth=1]\n\t"OUTER INPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT" -> "OBJECTIVE MECHANISM" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM" -> CONTROLLER [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\tCONTROLLER [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [color=orange penwidth=3 rank=min shape=oval]\n\t\t"INNER INPUT" [color=green penwidth=3 rank=source shape=oval]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> "INNER INPUT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM" -> Target [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"INNER OUTPUT" -> "NESTED COMPOSITION OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [color=orange penwidth=1 rank=min shape=oval]\n\t\t"INNER OUTPUT" -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget -> Comparator [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=orange penwidth=1 rank=min shape=oval]\n\t\tComparator -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [color=red penwidth=3 rank=max shape=oval]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ] From 86207c080b1d08dfd30b6ab72e005e12c547f3f9 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 13 Mar 2022 18:01:20 -0400 Subject: [PATCH 256/285] llvm, functions/LinearCombination,Reduce: Add human readable names to 'alloca' operations Signed-off-by: Jan Vesely --- .../components/functions/nonstateful/combinationfunctions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/combinationfunctions.py b/psyneulink/core/components/functions/nonstateful/combinationfunctions.py index e6d087c908e..e91fd02a118 100644 --- a/psyneulink/core/components/functions/nonstateful/combinationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/combinationfunctions.py @@ -894,7 +894,7 @@ def _gen_llvm_combine(self, builder, index, ctx, vi, vo, params): else: assert False, "Unknown operation: {}".format(operation) - val_p = builder.alloca(val.type) + val_p = builder.alloca(val.type, name="reduced_result") builder.store(val, val_p) pow_f = ctx.get_builtin("pow", [ctx.float_ty]) @@ -1428,7 +1428,7 @@ def _gen_llvm_combine(self, builder, index, ctx, vi, vo, params): else: assert False, "Unknown operation: {}".format(operation) - val_p = builder.alloca(val.type) + val_p = builder.alloca(val.type, name="combined_result") builder.store(val, val_p) pow_f = ctx.get_builtin("pow", [ctx.float_ty]) From 439f2457e3be516f517f16c4fb330dc2b7cf73ba Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 13 Mar 2022 16:33:38 -0400 Subject: [PATCH 257/285] llvm, mechanism: Update execution counter for all TimeScale values Signed-off-by: Jan Vesely --- psyneulink/core/components/mechanisms/mechanism.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 778eaa033b2..81ef4033dce 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -3090,8 +3090,11 @@ def _gen_llvm_function_internal(self, ctx, builder, m_params, m_state, arg_in, # Update internal clock (i.e. num_executions parameter) num_executions_ptr = pnlvm.helpers.get_state_ptr(builder, self, m_state, "num_executions") - for scale in [TimeScale.TIME_STEP, TimeScale.PASS, TimeScale.TRIAL, TimeScale.RUN]: - num_exec_time_ptr = builder.gep(num_executions_ptr, [ctx.int32_ty(0), ctx.int32_ty(scale.value)]) + for scale in TimeScale: + assert scale.value < len(num_executions_ptr.type.pointee) + num_exec_time_ptr = builder.gep(num_executions_ptr, + [ctx.int32_ty(0), ctx.int32_ty(scale.value)], + name="num_executions_{}_ptr".format(scale)) new_val = builder.load(num_exec_time_ptr) new_val = builder.add(new_val, new_val.type(1)) builder.store(new_val, num_exec_time_ptr) From 074903a7df6d03a879c46a909c1859c81c883815 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 26 Mar 2022 20:44:01 -0400 Subject: [PATCH 258/285] llvm, mechanism: Refactor parsing of output port specification Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 81ef4033dce..7cdcc51d4ac 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -3023,14 +3023,21 @@ def _gen_llvm_output_port_parse_variable(self, ctx, builder, elif port_spec == OWNER_EXECUTION_COUNT: execution_count = pnlvm.helpers.get_state_ptr(builder, self, mech_state, EXECUTION_COUNT) return execution_count - elif isinstance(port_spec, tuple) and port_spec[0] == OWNER_VALUE: - index = port_spec[1]() if callable(port_spec[1]) else port_spec[1] - assert index < len(value.type.pointee) - return builder.gep(value, [ctx.int32_ty(0), ctx.int32_ty(index)]) + try: + name = port_spec[0] + ids = (x() if callable(x) else x for x in port_spec[1:]) + except TypeError as e: + # TypeError means we can't index. + # Convert this to assertion failure below + pass else: #TODO: support more spec options - assert False, "Unsupported OutputPort spec: {} ({})".format(port_spec, value.type) + if name == OWNER_VALUE: + data = value + return builder.gep(data, [ctx.int32_ty(0), *(ctx.int32_ty(i) for i in ids)]) + + assert False, "Unsupported OutputPort spec: {} ({})".format(port_spec, value.type) def _gen_llvm_output_ports(self, ctx, builder, value, mech_params, mech_state, mech_in, mech_out): From 7612cb7d4e7eb1e5f1fa96fdd0adbd794d801b97 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 26 Mar 2022 21:05:16 -0400 Subject: [PATCH 259/285] tests/OutputPort: Split output port composition testing into parametrized subtests Signed-off-by: Jan Vesely --- tests/ports/test_output_ports.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/tests/ports/test_output_ports.py b/tests/ports/test_output_ports.py index 6551be99e9b..90c364ce04b 100644 --- a/tests/ports/test_output_ports.py +++ b/tests/ports/test_output_ports.py @@ -7,7 +7,6 @@ class TestOutputPorts: @pytest.mark.mechanism - @pytest.mark.lca_mechanism def test_output_port_variable_spec(self, mech_mode): # Test specification of OutputPort's variable mech = pnl.ProcessingMechanism(default_variable=[[1.],[2.],[3.]], @@ -33,28 +32,28 @@ def test_output_port_variable_spec(self, mech_mode): assert np.array_equal(i, e) @pytest.mark.mechanism - @pytest.mark.lca_mechanism - def test_output_port_variable_spec_composition(self, comp_mode): + @pytest.mark.parametrize('spec, expected1, expected2', + [((pnl.OWNER_VALUE, 0), [1], [1]), + ((pnl.OWNER_VALUE, 1), [2], [2]), + ((pnl.OWNER_VALUE, 2), [3], [3]), + pytest.param((pnl.OWNER_VALUE, 3), [3], [3], marks=[pytest.mark.xfail()]), + ((pnl.OWNER_EXECUTION_COUNT), [1], [2]), + ], ids=lambda x: str(x) if len(x) != 1 else '') + def tests_output_port_variable_spec_composition(self, comp_mode, spec, expected1, expected2): # Test specification of OutputPort's variable # OutputPort mech.output_ports['all'] has a different dimensionality than the other OutputPorts; # as a consequence, when added as a terminal node, the Composition can't construct an IDENTITY_MATRIX # from the mech's OutputPorts to the Composition's output_CIM. # FIX: Remove the following line and correct assertions below once above condition is resolved - mech = pnl.ProcessingMechanism(default_variable=[[1.],[2.],[3.]], - name='MyMech', - output_ports=[ - pnl.OutputPort(name='z', variable=(pnl.OWNER_VALUE, 2)), - pnl.OutputPort(name='y', variable=(pnl.OWNER_VALUE, 1)), - pnl.OutputPort(name='x', variable=(pnl.OWNER_VALUE, 0)), -# pnl.OutputPort(name='all', variable=(pnl.OWNER_VALUE)), - pnl.OutputPort(name='execution count', variable=(pnl.OWNER_EXECUTION_COUNT)) - ]) + var = [[1], [2], [3]] + mech = pnl.ProcessingMechanism(default_variable=var, name='MyMech', + output_ports=[pnl.OutputPort(variable=spec)]) C = pnl.Composition(name='MyComp') C.add_node(node=mech) - outs = C.run(inputs={mech: [[1.],[2.],[3.]]}, execution_mode=comp_mode) - assert np.array_equal(outs, [[3], [2], [1], [1]]) - outs = C.run(inputs={mech: [[1.],[2.],[3.]]}, execution_mode=comp_mode) - assert np.array_equal(outs, [[3], [2], [1], [2]]) + outs = C.run(inputs={mech: var}, execution_mode=comp_mode) + assert np.allclose(outs, expected1) + outs = C.run(inputs={mech: var}, execution_mode=comp_mode) + assert np.allclose(outs, expected2) def test_no_path_afferents(self): A = pnl.OutputPort() From b7e7181ad9ae080921ac88209d3226fcd01661c6 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 26 Mar 2022 22:47:29 -0400 Subject: [PATCH 260/285] test/OutputPort: Use multiple passes and trials in the test This will exercise execution counts at different time scales Signed-off-by: Jan Vesely --- tests/ports/test_output_ports.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/ports/test_output_ports.py b/tests/ports/test_output_ports.py index 90c364ce04b..3f8bb230402 100644 --- a/tests/ports/test_output_ports.py +++ b/tests/ports/test_output_ports.py @@ -37,8 +37,9 @@ def test_output_port_variable_spec(self, mech_mode): ((pnl.OWNER_VALUE, 1), [2], [2]), ((pnl.OWNER_VALUE, 2), [3], [3]), pytest.param((pnl.OWNER_VALUE, 3), [3], [3], marks=[pytest.mark.xfail()]), - ((pnl.OWNER_EXECUTION_COUNT), [1], [2]), + ((pnl.OWNER_EXECUTION_COUNT), [4], [8]), ], ids=lambda x: str(x) if len(x) != 1 else '') + @pytest.mark.usefixtures("comp_mode_no_llvm") def tests_output_port_variable_spec_composition(self, comp_mode, spec, expected1, expected2): # Test specification of OutputPort's variable # OutputPort mech.output_ports['all'] has a different dimensionality than the other OutputPorts; @@ -50,9 +51,10 @@ def tests_output_port_variable_spec_composition(self, comp_mode, spec, expected1 output_ports=[pnl.OutputPort(variable=spec)]) C = pnl.Composition(name='MyComp') C.add_node(node=mech) - outs = C.run(inputs={mech: var}, execution_mode=comp_mode) + C.termination_processing[pnl.TimeScale.TRIAL] = pnl.AtPass(2) + outs = C.run(inputs={mech: var}, num_trials=2, execution_mode=comp_mode) assert np.allclose(outs, expected1) - outs = C.run(inputs={mech: var}, execution_mode=comp_mode) + outs = C.run(inputs={mech: var}, num_trials=2, execution_mode=comp_mode) assert np.allclose(outs, expected2) def test_no_path_afferents(self): From c79f7bb40d44c437840449fb13a14830cc4d5492 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 27 Mar 2022 10:11:22 -0400 Subject: [PATCH 261/285] Refactor/ocm/state features all as input ports (#2360) --- .../core/components/mechanisms/mechanism.py | 2 +- .../control/optimizationcontrolmechanism.py | 247 ++++++++++-------- psyneulink/core/compositions/composition.py | 9 +- psyneulink/core/globals/registry.py | 24 +- tests/composition/test_control.py | 242 ++++++++--------- tests/composition/test_show_graph.py | 70 ++--- 6 files changed, 294 insertions(+), 300 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 778eaa033b2..cc34172fe18 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -3442,7 +3442,7 @@ def port_cell(port, include_function:bool=False, include_value:bool=False, use_l if use_label and not isinstance(port, ParameterPort): value = f'
={port.labeled_value}' else: - value = f'
={port.labeled_value}' + value = f'
={port.value}' return f'{port.name}{function}{value}' diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index b1738d15e5c..071495fe883 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1060,8 +1060,9 @@ from psyneulink.core.globals.defaults import defaultControlAllocation from psyneulink.core.globals.keywords import \ ALL, COMPOSITION, COMPOSITION_FUNCTION_APPROXIMATOR, CONCATENATE, DEFAULT_INPUT, DEFAULT_VARIABLE, EID_FROZEN, \ - FUNCTION, INTERNAL_ONLY, NAME, OPTIMIZATION_CONTROL_MECHANISM, NODE, OWNER_VALUE, PARAMS, PORT, PROJECTIONS, \ - SHADOW_INPUTS, SHADOW_INPUT_NAME, VALUE + FUNCTION, INPUT_PORT, INTERNAL_ONLY, NAME, OPTIMIZATION_CONTROL_MECHANISM, NODE, OWNER_VALUE, PARAMS, PORT, \ + PROJECTIONS, SHADOW_INPUTS, SHADOW_INPUT_NAME, VALUE +from psyneulink.core.globals.registry import rename_instance_in_registry from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.sampleiterator import SampleIterator, SampleSpec @@ -1070,9 +1071,12 @@ __all__ = [ 'OptimizationControlMechanism', 'OptimizationControlMechanismError', - 'AGENT_REP', 'STATE_FEATURES', 'STATE_FEATURE_FUNCTION', 'RANDOMIZATION_CONTROL_SIGNAL', 'NUM_ESTIMATES' + 'AGENT_REP', 'STATE_FEATURES', 'STATE_FEATURE_FUNCTION', 'RANDOMIZATION_CONTROL_SIGNAL', 'NUM_ESTIMATES', + 'DEFERRED_STATE_INPUT_PORT_PREFIX', 'NUMERIC_STATE_INPUT_PORT_PREFIX', 'SHADOWED_INPUT_STATE_INPUT_PORT_PREFIX', + 'INPUT_SOURCE_FOR_STATE_INPUT_PORT_PREFIX' ] +# constructor arguments AGENT_REP = 'agent_rep' STATE_FEATURES = 'state_features' STATE_FEATURE_FUNCTION = 'state_feature_function' @@ -1080,14 +1084,30 @@ RANDOM_VARIABLES = 'random_variables' NUM_ESTIMATES = 'num_estimates' +# state_input_port names +NUMERIC_STATE_INPUT_PORT_PREFIX = "NUMERIC INPUT FOR " +INPUT_SOURCE_FOR_STATE_INPUT_PORT_PREFIX = "SOURCE OF INPUT FOR " +SHADOWED_INPUT_STATE_INPUT_PORT_PREFIX = "SHADOWED INPUT OF " +# SHADOWED_INPUT_STATE_INPUT_PORT_PREFIX = "Shadowed input of " +DEFERRED_STATE_INPUT_PORT_PREFIX = 'DEFERRED INPUT NODE InputPort ' -def deferred_state_feature_node_msg(node_name, agent_rep_name): - return f"DEFERRED {node_name} OF {agent_rep_name}" +def _state_input_port_name(source_port_name, agent_rep_input_port_name): + return f"INPUT FROM {source_port_name} FOR {agent_rep_input_port_name}" -def deferred_state_feature_spec_msg(spec_str, comp_name): +def _shadowed_state_input_port_name(shadowed_port_name, agent_rep_input_port_name): + return f"{SHADOWED_INPUT_STATE_INPUT_PORT_PREFIX}{shadowed_port_name} FOR {agent_rep_input_port_name}" + +def _numeric_state_input_port_name(agent_rep_input_port_name): + return f"{NUMERIC_STATE_INPUT_PORT_PREFIX}{agent_rep_input_port_name}" + +def _deferred_agent_rep_input_port_name(node_name, agent_rep_name): + # return f"{DEFERRED_STATE_INPUT_PORT_PREFIX}{node_name} OF {agent_rep_name}" + return f"{DEFERRED_STATE_INPUT_PORT_PREFIX}OF {agent_rep_name} ({node_name})" + +def _deferred_state_feature_spec_msg(spec_str, comp_name): return f"{spec_str} NOT (YET) IN {comp_name}" -def not_specified_state_feature_spec_msg(spec_str, comp_name): +def _not_specified_state_feature_spec_msg(spec_str, comp_name): return f"NO SPECIFICATION (YET) FOR {spec_str} IN {comp_name}" def _state_feature_values_getter(owning_component=None, context=None): @@ -1131,16 +1151,16 @@ def _state_feature_values_getter(owning_component=None, context=None): # Get key if not isinstance(key, InputPort): # INPUT Node InputPort is not fully or properly specified - key = deferred_state_feature_node_msg((key or str(i - num_agent_rep_input_ports)), + key = _deferred_agent_rep_input_port_name((key or str(i - num_agent_rep_input_ports)), owning_component.agent_rep.name) elif key not in owning_component._get_agent_rep_input_receivers(): # INPUT Node InputPort is not (yet) in agent_rep - key = deferred_state_feature_node_msg(key.full_name, owning_component.agent_rep.name) + key = _deferred_agent_rep_input_port_name(key.full_name, owning_component.agent_rep.name) # Get state_feature_value if spec is None: # state_feature not specified; default input will be assigned in _instantiate_input_dict() - state_feature_value = not_specified_state_feature_spec_msg((key if isinstance(key, str) else key.full_name), + state_feature_value = _not_specified_state_feature_spec_msg((key if isinstance(key, str) else key.full_name), owning_component.composition.name) elif is_numeric(spec): # if spec is numeric, use that @@ -1148,7 +1168,7 @@ def _state_feature_values_getter(owning_component=None, context=None): elif (hasattr(owning_component, 'composition') and not owning_component.composition._is_in_composition(spec)): # spec is not in ocm.composition - state_feature_value = deferred_state_feature_spec_msg(spec.full_name, owning_component.agent_rep.name) + state_feature_value = _deferred_state_feature_spec_msg(spec.full_name, owning_component.agent_rep.name) elif state_input_port.parameters.value._get(context) is not None: # if state_input_port returns a value, use that state_feature_value = state_input_port.parameters.value._get(context) @@ -1845,7 +1865,6 @@ def _instantiate_input_ports(self, context=None): This instantiates the OptimizationControlMechanism's `state_input_ports; these are used to provide input to the agent_rep when its evaluate method is called - (see Composition._build_predicted_inputs_dict). The OptimizationControlMechanism's outcome_input_ports are instantiated by ControlMechanism._instantiate_input_ports in the call to super(). @@ -2126,10 +2145,13 @@ def _parse_specs(state_feature_specs, specified_input_ports=None, spec_type="lis """ parsed_feature_specs = [] - num_specs = len(state_feature_specs) + num_user_specs = len(state_feature_specs) num_specified_ports = len(specified_input_ports) num_agent_rep_input_ports = len(agent_rep_input_ports) - assert num_specs == num_specified_ports, f"ALERT: num state_feature_specs != num ports in _parse_spec()" + # Total number of specs to be parsed: + self._num_state_feature_specs = max(num_user_specs, num_agent_rep_input_ports) + + assert num_user_specs == num_specified_ports, f"ALERT: num state_feature_specs != num ports in _parse_spec()" # Note: there may be more state_feature_specs (i.e., ones for unspecified input_ports) # than num_specified_ports @@ -2148,7 +2170,7 @@ def _parse_specs(state_feature_specs, specified_input_ports=None, spec_type="lis f"inputs.") # Too MANY specs for number of agent_rep receivers - if num_specs > num_agent_rep_input_ports: + if num_user_specs > num_agent_rep_input_ports: # specs_not_in_agent_rep = [f"'{spec.name if isinstance(spec, Mechanism) else spec.owner.name}'" # for spec in self._get_specs_not_in_agent_rep(state_feature_specs)] specs_not_in_agent_rep = \ @@ -2185,71 +2207,87 @@ def _parse_specs(state_feature_specs, specified_input_ports=None, spec_type="lis f"shadowed.") state_input_port_names = [] - self._num_state_feature_specs = max(num_specs, num_agent_rep_input_ports) for i in range(self._num_state_feature_specs): - # PORT & PORT_NAME - # (and specs for CFA and any Nodes not yet in agent_rep) state_input_port_name = None state_feature_fct = None + + # FIX: CONSOLIDATE THIS WITH PARSING OF SPEC BELOW + # AGENT_REP INPUT NODE InputPort + # Assign it's name to be used in state_features + # (and specs for CFA and any Nodes not yet in agent_rep) if self.agent_rep_type == COMPOSITION: - # Process number of specs for which there are known INPUT Ports of agent_rep if i < num_agent_rep_input_ports: - # Just get port and port name (spec will be parsed and assigned below) - # Node should be in agent_rep, so use that to be sure - if self.agent_rep_type == COMPOSITION: - port = agent_rep_input_ports[i] - port_name = port.full_name - # For Nodes not (yet) in agent_rep: + # spec is for Input{ort of INPUT Node already in agent_rep + # so get agent_rep_input_port and its name (spec will be parsed and assigned below) + agent_rep_input_port = agent_rep_input_ports[i] + agent_rep_input_port_name = agent_rep_input_port.full_name else: - # - get specified value for spec, for later parsing and assignment (once Node is known) - spec = state_feature_specs[i] - port = specified_input_ports[i] + # spec is for deferred NODE InputPort (i.e., not (yet) in agent_rep) + # so get specified value for spec, for later parsing and assignment (once Node is known) + agent_rep_input_port = specified_input_ports[i] # - assign "DEFERRED n" as node name - state_input_port_name = f'DEFFERED {str(i-num_agent_rep_input_ports)}' - # For CompositionFunctionApproximator, assign spec as port + agent_rep_input_port_name = \ + _deferred_agent_rep_input_port_name(str(i - num_agent_rep_input_ports), + self.agent_rep.name) + # For CompositionFunctionApproximator, assign spec as agent_rep_input_port else: spec = state_feature_specs[i] - port = spec if isinstance(spec, (Mechanism, Composition)) else spec.owner + agent_rep_input_port = spec + agent_rep_input_port_name = spec.full_name if isinstance(spec, Port) else spec.name + # Assign state_input_port_name here as won't get done below (i can't be < num_user_specs for CFA) state_input_port_name = f"FEATURE {i} FOR {self.agent_rep.name}" - # SPEC - # Pare and assign specs for INPUT Nodes already in agent_rep (i.e., unassigned above) - # (others may be added to Composition later) - if i < num_specs: + # SPEC and state_input_port_name + # Parse and assign user specifications (note: may be for INPUT Node InputPorts not yet inagent_rep) + if i < num_user_specs: # i.e., if num_agent_rep_input_ports < num_user_specs) spec = state_feature_specs[i] - # Assign input_port name + # Unpack tuple + if isinstance(spec, tuple): state_feature_fct = spec[1] spec = spec[0] + + # Assign spec and state_input_port name if is_numeric(spec): - state_input_port_name = f"{port_name} {DEFAULT_VARIABLE.upper()}" - elif isinstance(spec, (Port, Mechanism, Composition)): - if hasattr(spec, 'full_name'): - state_input_port_name = spec.full_name - else: - state_input_port_name = spec.name + state_input_port_name = _numeric_state_input_port_name(agent_rep_input_port_name) + + elif isinstance(spec, (InputPort, Mechanism)): + spec_name = spec.full_name if isinstance(spec, InputPort) else spec.input_port.full_name + state_input_port_name = _shadowed_state_input_port_name(spec_name, + agent_rep_input_port_name) + elif isinstance(spec, OutputPort): + state_input_port_name = _state_input_port_name(spec.full_name, + agent_rep_input_port_name) + elif isinstance(spec, Composition): + assert False, f"Composition spec ({spec}) made it to _parse_specs for {self.name}." + + elif spec == SHADOW_INPUTS: + # Shadow the specified agent_rep_input_port (Name assigned where shadow input is parsed) + spec = agent_rep_input_port + state_input_port_name = _shadowed_state_input_port_name(agent_rep_input_port_name, + agent_rep_input_port_name) + elif isinstance(spec, dict): - state_input_port_name = spec[NAME] if NAME in spec else f"STATE FEATURE INPUT for {port_name}" + state_input_port_name = spec[NAME] if NAME in spec else f"INPUT FOR {agent_rep_input_port_name}" # tuple specification of function (assigned above) overrides dictionary specification if state_feature_fct is None: if FUNCTION in spec: state_feature_fct = spec[FUNCTION] elif PARAMS in spec and FUNCTION in spec[PARAMS]: state_feature_fct = spec[PARAMS][FUNCTION] - elif spec == SHADOW_INPUTS: - # Shadow the specified port - spec = port + elif spec is not None: assert False, f"PROGRAM ERROR: unrecognized form of state_feature specification for {self.name}" - # Fewer specifications than number of INPUT Nodes, so assign state_feature_default + # Fewer specifications than number of INPUT Nodes, so assign state_feature_default to the rest else: + # Note: state_input_port name assigned above spec = self.state_feature_default parsed_feature_specs.append(spec) self._state_feature_functions.append(state_feature_fct) - self._specified_INPUT_Node_InputPorts_in_order.append(port) + self._specified_INPUT_Node_InputPorts_in_order.append(agent_rep_input_port) state_input_port_names.append(state_input_port_name) if not any(self._state_feature_functions): @@ -2265,7 +2303,7 @@ def _parse_specs(state_feature_specs, specified_input_ports=None, spec_type="lis # SINGLE ITEM spec, SO APPLY TO ALL agent_rep_input_ports if (user_specs is None or isinstance(user_specs, (str, tuple, InputPort, OutputPort, Mechanism, Composition)) - or is_numeric(user_specs)): + or (is_numeric(user_specs) and (np.array(user_specs).ndim < 2))): specs = [user_specs] * len(agent_rep_input_ports) # OK to assign here (rather than in _parse_secs()) since spec is intended for *all* state_input_ports self.parameters.state_feature_specs.set(specs, override=True) @@ -2373,11 +2411,7 @@ def _parse_specs(state_feature_specs, specified_input_ports=None, spec_type="lis # # Get specified ports in order of agent_rep INPUT Nodes, with None assigned to any unspecified InputPorts all_specified_ports = [port if port in expanded_specified_ports - # MODIFIED 3/24/22 OLD: else None for port in agent_rep_input_ports] - # MODIFIED 3/24/22 NEW: - # else self.state_feature_default for port in agent_rep_input_ports] - # MODIFIED 3/24/22 END # Get any not found anywhere (including nested) in agent_rep, which are placed at the end of list all_specified_ports.extend([port for port in expanded_specified_ports if port not in agent_rep_input_ports]) @@ -2469,19 +2503,15 @@ def _validate_entries(spec=None, source=None): spec = get_port_for_mech_spec(spec) self.state_feature_specs[i] = spec + # Get InputPort specification dictionary for state_input_port and update its entries parsed_spec = _parse_port_spec(owner=self, port_type=InputPort, port_spec=spec) - - if not parsed_spec[NAME]: - parsed_spec[NAME] = state_input_port_names[i] - + parsed_spec[NAME] = state_input_port_names[i] if parsed_spec[PARAMS] and SHADOW_INPUTS in parsed_spec[PARAMS]: # Composition._update_shadow_projections will take care of PROJECTIONS specification parsed_spec[PARAMS][INTERNAL_ONLY]=True, parsed_spec[PARAMS][PROJECTIONS]=None - # Assign function for state_input_port if specified--------------------------------------------------- parsed_spec = self._assign_state_feature_function(parsed_spec, i) - parsed_spec = [parsed_spec] # so that extend works below state_input_port_specs.extend(parsed_spec) @@ -2503,12 +2533,7 @@ def _assign_state_feature_function(self, specification_dict, idx=None): assert len(self._state_feature_functions) == self._num_state_feature_specs, \ f"PROGRAM ERROR: Length of _state_feature_functions for {self.name} should be same " \ f"as number of state_input_port_dicts passed to _assign_state_feature_function" - # # MODIFIED 3/24/22 OLD: - # state_feature_functions = (self._state_feature_functions - # if self._state_feature_functions[0] is not None else None) - # MODIFIED 3/24/22 NEW: state_feature_functions = self._state_feature_functions - # MODIFIED 3/24/22 END except AttributeError: # state_features assigned automatically in _update_state_input_ports_for_controller, # so _state_feature_functions (for individual state_features) not created @@ -2534,7 +2559,8 @@ def _update_state_input_ports_for_controller(self, context=None): (note: validation of state_features specified for CompositionFunctionApproximator optimization is up to the CompositionFunctionApproximator) - For agent_rep that is a Composition, call: + If agent_rep is a Composition, call: + - _update_state_input_port_names() - _update_state_features_dict() - _validate_state_features() """ @@ -2565,14 +2591,13 @@ def _update_state_input_ports_for_controller(self, context=None): # assert self.num_state_input_ports < len(self.agent_rep_input_ports), \ # f"PROGRAM ERROR: More state_input_ports assigned to '{self.name}' ({self.num_state_input_ports}) " \ # f"than agent_rep ('{self.agent_rep.name}') has INPUT Node InputPorts ({num_agent_rep_input_ports})." - # FIX: 3/24/22 - SHOULD PROBABLY REFACTOR THIS TO CALL _parse_state_feature_specs + # FIX: 3/24/22 - REFACTOR THIS TO CALL _parse_state_feature_specs? state_input_ports = [] local_context = Context(source=ContextFlags.METHOD) default = self.state_feature_default new_agent_rep_input_ports = self.agent_rep_input_ports[self.num_state_input_ports:] for input_port in new_agent_rep_input_ports: # Instantiate state_input_port for each agent_rep INPUT Node InputPort not already specified: - # OLD: [MODIFIED] params = {INTERNAL_ONLY:True, PARAMS: {}} if default is None: @@ -2583,8 +2608,7 @@ def _update_state_input_ports_for_controller(self, context=None): self.state_feature_specs.append(input_port) elif is_numeric(default): params[VALUE]: default - # FIX: 3/24/22 - NEED TO STANDARDIZE NAME FOR NUMERIC spec AND ALIGN WITH OTHER USES - input_port_name = f"NUMERIC INPUT FOR {input_port.full_name}]" + input_port_name = _numeric_state_input_port_name(input_port.full_name) self.state_feature_specs.append(default) elif isinstance(default, (Port, Mechanism, Composition)): params[PROJECTIONS]: default @@ -2609,43 +2633,40 @@ def _update_state_input_ports_for_controller(self, context=None): # Assign OptimizationControlMechanism attributes self.state_input_ports.extend(state_input_ports) - # FIX: 3/24/22 - ?OK: - # self._num_state_feature_specs = len(self.state_input_ports) self._specified_INPUT_Node_InputPorts_in_order = self.agent_rep_input_ports - # MODIFIED 3/24/22 END - - if self.state_feature_specs: - # Restrict validation and any further instantiation of state_input_ports - # until run time, when the Composition is expected to be fully constructed - if context._execution_phase == ContextFlags.PREPARING: - # FIX: 1/30/22 - NEEDS TO EXECUTE ON UPDATES WITHOUT RUN, - # BUT MANAGE ERRORS WRT TO _validate_state_features - self._update_state_features_dict() - self._validate_state_features(context) - - def _update_state_features_dict(self): - agent_rep_input_ports = self._get_agent_rep_input_receivers() - specified_input_ports = self._specified_INPUT_Node_InputPorts_in_order - - for i, port in enumerate(self.state_input_ports): - # Get value (need first, to determine whether it belongs to a nested Comp, for assigning key) - feature = self.state_feature_specs[i] - # Get INPUT Node of agent_rep as key: - if (isinstance(feature, Component) and - feature.owner in [n[0] for n in self.agent_rep._get_nested_nodes()]): - node = feature.owner - elif specified_input_ports[i]: - node = specified_input_ports[i] - elif i < len(agent_rep_input_ports): - node = specified_input_ports[i] = agent_rep_input_ports[i] - else: - node = None - if not (isinstance(node, str) and 'DEFERRED' in node): - continue - if feature.owner not in self._get_agent_rep_input_receivers(): - # Don't add to dict, will be dealt with or raise an error at run time + + self._update_state_input_port_names() + + if context._execution_phase == ContextFlags.PREPARING: + # Restrict validation until run time, when the Composition is expected to be fully constructed + self._validate_state_features(context) + + def _update_state_input_port_names(self): + """Update names of state_input_port for any newly instantiated INPUT Node InputPorts + """ + for i, state_input_port in enumerate(self.state_input_ports): + + if i < len(self.agent_rep_input_ports): + self._specified_INPUT_Node_InputPorts_in_order[i] = self.agent_rep_input_ports[i] + + if i == len(self.agent_rep_input_ports): + # All state_input_ports beyond number of agent_rep_input_ports must be for deferred nodes + assert DEFERRED_STATE_INPUT_PORT_PREFIX in state_input_port.name, \ + f"PROGRAM ERROR: {state_input_port.name} should have 'DEFERRED' in its name." continue - self.state_feature_specs[i] = feature + if state_input_port.path_afferents and DEFERRED_STATE_INPUT_PORT_PREFIX in state_input_port.name: + agent_rep_input_port_name = self.agent_rep_input_ports[i].full_name + source_input_port_name = self.state_feature_specs[i].full_name + if 'INPUT FROM' in state_input_port.name: + new_name = _state_input_port_name(source_input_port_name, agent_rep_input_port_name) + elif NUMERIC_STATE_INPUT_PORT_PREFIX in state_input_port.name: + new_name = _numeric_state_input_port_name(agent_rep_input_port_name) + elif SHADOWED_INPUT_STATE_INPUT_PORT_PREFIX in state_input_port.name: + new_name = _shadowed_state_input_port_name(source_input_port_name, agent_rep_input_port_name) + state_input_port.name = rename_instance_in_registry(registry=self._portRegistry, + category=INPUT_PORT, + new_name= new_name, + component=state_input_port) def _validate_state_features(self, context): """Validate that state_features are legal and consistent with agent_rep. @@ -2734,7 +2755,7 @@ def _validate_state_features(self, context): invalid_state_features = [input_port for input_port in self.state_input_ports if (input_port.shadow_inputs and not (input_port.shadow_inputs.owner - in self._get_agent_rep_input_receivers()) + in self.agent_rep_input_ports) and (isinstance(input_port.shadow_inputs.owner, CompositionInterfaceMechanism) and not (input_port.shadow_inputs.owner.composition in @@ -3443,6 +3464,10 @@ def num_state_input_ports(self): except: return 0 + # @property + # def _num_state_feature_specs(self): + # return len(self.state_feature_specs) + @property def state_features(self): """Return {InputPort name: source name} for all state_features. @@ -3456,7 +3481,9 @@ def state_features(self): (it should be resolved by runtime, or an error is generated). """ - self._update_state_features_dict() + # self._update_state_features_dict() + self._update_state_input_port_names() + agent_rep_input_ports = self.agent_rep.external_input_ports_of_all_input_nodes state_features_dict = {} state_input_port_num = 0 @@ -3471,9 +3498,9 @@ def state_features(self): key = input_port.full_name else: # Specified InputPort is not (yet) in agent_rep - input_port_name = (f"{input_port.full_name} AS INPUT NODE" if input_port - else f"INPUT NODE {str(i-len(agent_rep_input_ports))}") - key = deferred_state_feature_node_msg(input_port_name, self.agent_rep.name) + input_port_name = (f"{input_port.full_name}" if input_port + else f"{str(i-len(agent_rep_input_ports))}") + key = _deferred_agent_rep_input_port_name(input_port_name, self.agent_rep.name) # Get source for state_features dict if spec is None: @@ -3491,7 +3518,7 @@ def state_features(self): if self.composition._is_in_composition(spec): source = spec.full_name else: - source = deferred_state_feature_spec_msg(spec.full_name, self.composition.name) + source = _deferred_state_feature_spec_msg(spec.full_name, self.composition.name) state_input_port_num += 1 state_features_dict[key] = source diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 1be8e26b4ce..270d1de5d9a 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -9444,9 +9444,6 @@ def run( # If they can not be initialized, raise a warning. self._complete_init_of_partially_initialized_nodes(context=context) - if self._need_check_for_unused_projections: - self._check_for_unused_projections(context=context) - if ContextFlags.SIMULATION_MODE not in context.runmode: self._check_controller_initialization_status() self._check_nodes_initialization_status() @@ -9454,6 +9451,9 @@ def run( if not skip_analyze_graph: self._analyze_graph(context=context) + if self._need_check_for_unused_projections: + self._check_for_unused_projections(context=context) + if scheduler is None: scheduler = self.scheduler @@ -10288,7 +10288,8 @@ def execute( if ContextFlags.SIMULATION_MODE in context.runmode and inputs is not None: self.input_CIM.execute(build_CIM_input, context=context) else: - assert inputs is None, "Ignoring composition input!" + assert inputs is None, f"Input provided to nested Composition {self.name}; run() method should " \ + f"only be called on outer-most composition within which it is nested." self.input_CIM.execute(context=context) self.parameter_CIM.execute(context=context) else: diff --git a/psyneulink/core/globals/registry.py b/psyneulink/core/globals/registry.py index 2586761aba4..15c585d1c7f 100644 --- a/psyneulink/core/globals/registry.py +++ b/psyneulink/core/globals/registry.py @@ -274,12 +274,12 @@ def register_instance(entry, name, base_class, registry, sub_dict): else: renamed_instance_counts[match.groups()[0]] += 1 -def rename_instance_in_registry(registry, category, name=None, component=None): +def rename_instance_in_registry(registry, category, new_name, old_name=None, component=None): """Rename instance in category registry Instance to be renamed can be specified by a reference to the component or its name. COMMENT: - DEPRECACTED (SEE IMPLEMENTATION NOTE BELOW) + DEPRECATED (SEE IMPLEMENTATION NOTE BELOW) If the name of the instance was a default name, and it was the last in the sequence, decrement renamed_instance_counts and if it was the only one, remove that name from the renamed_instance list COMMENT @@ -287,24 +287,27 @@ def rename_instance_in_registry(registry, category, name=None, component=None): registry_entry = registry[category] - if not (name or component): + if not (old_name or component): raise RegistryError("Must specify a name or component to remove an entry of {}". format(registry.__class__.__name__)) - if (name and component) and name != component.name: + if (old_name and component) and old_name != component.old_name: raise RegistryError("Conflicting name ({}) and component ({}) specified for entry to remove from {}". - format(name, component.name, registry.__class__.__name__)) - if component and not name: + format(old_name, component.old_name, registry.__class__.__name__)) + if component and not old_name: for n, c in registry_entry.instanceDict.items(): if component == c: - name = n + old_name = n try: - clear_registry(registry_entry.instanceDict[name]._portRegistry) + clear_registry(registry_entry.instanceDict[old_name]._portRegistry) except (AttributeError): pass - # Delete instance - del registry_entry.instanceDict[name] + entry = registry_entry.instanceDict[old_name] + # Delete entry for instance + del registry_entry.instanceDict[old_name] + # Add entry for instance under new name + registry_entry.instanceDict[new_name] = entry # Decrement count for instances in entry instance_count = registry_entry.instanceCount - 1 @@ -327,6 +330,7 @@ def rename_instance_in_registry(registry, category, name=None, component=None): instance_count, registry_entry.renamed_instance_counts, registry_entry.default) + return new_name def remove_instance_from_registry(registry, category, name=None, component=None): """Remove instance from registry category entry diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index b58d8cae590..8b87a084e10 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -8,6 +8,9 @@ from psyneulink.core.globals.log import LogCondition from psyneulink.core.globals.sampleiterator import SampleIterator, SampleIteratorError, SampleSpec from psyneulink.core.globals.utilities import _SeededPhilox +from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import \ + _deferred_agent_rep_input_port_name, _deferred_state_feature_spec_msg, \ + _state_input_port_name, _numeric_state_input_port_name, _shadowed_state_input_port_name @pytest.mark.control class TestControlSpecification: @@ -210,24 +213,30 @@ def test_deferred_init(self, control_spec, state_features_arg): ) assert any(expected_warning in repr(w.message) for w in warning.list) + deferred_reward_input_port = _deferred_state_feature_spec_msg('reward[InputPort-0]', 'evc') + deferred_Input_input_port = _deferred_state_feature_spec_msg('Input[InputPort-0]', 'evc') + deferred_node_0 = _deferred_agent_rep_input_port_name('0','evc') + deferred_node_1 = _deferred_agent_rep_input_port_name('1','evc') + deferred_shadowed_0 = _shadowed_state_input_port_name('reward[InputPort-0]' , deferred_node_0) + deferred_shadowed_1 = _shadowed_state_input_port_name('Input[InputPort-0]' , deferred_node_1) + deferred_reward_node = _deferred_agent_rep_input_port_name('reward[InputPort-0]','evc') + deferred_Input_node = _deferred_agent_rep_input_port_name('Input[InputPort-0]','evc') + shadowed_reward_node = _shadowed_state_input_port_name('reward[InputPort-0]' ,'reward[InputPort-0]') + shadowed_Input_node = _shadowed_state_input_port_name('Input[InputPort-0]' ,'Input[InputPort-0]') + assert comp._controller_initialization_status == pnl.ContextFlags.DEFERRED_INIT - if state_features_arg == 'list': - assert comp.controller.state_features == {'DEFERRED INPUT NODE 0 OF evc': - 'reward[InputPort-0] NOT (YET) IN evc', - 'DEFERRED INPUT NODE 1 OF evc': - 'Input[InputPort-0] NOT (YET) IN evc'} - assert comp.controller.state_feature_values == {'DEFERRED 0 OF evc': 'reward[InputPort-0] NOT (YET) IN evc', - 'DEFERRED 1 OF evc': 'Input[InputPort-0] NOT (YET) IN evc'} + assert comp.controller.state_input_ports.names == [deferred_shadowed_0, deferred_shadowed_1] + if state_features_arg == 'list': + assert comp.controller.state_features == {deferred_node_0: deferred_reward_input_port, + deferred_node_1: deferred_Input_input_port} + assert comp.controller.state_feature_values == {deferred_node_0: deferred_reward_input_port, + deferred_node_1: deferred_Input_input_port} elif state_features_arg == 'dict': - assert comp.controller.state_features == {'DEFERRED reward[InputPort-0] AS INPUT NODE OF evc': - 'reward[InputPort-0] NOT (YET) IN evc', - 'DEFERRED Input[InputPort-0] AS INPUT NODE OF evc': - 'Input[InputPort-0] NOT (YET) IN evc'} - assert comp.controller.state_feature_values == {'DEFERRED reward[InputPort-0] OF evc': - 'reward[InputPort-0] NOT (YET) IN evc', - 'DEFERRED Input[InputPort-0] OF evc': - 'Input[InputPort-0] NOT (YET) IN evc'} + assert comp.controller.state_features == {deferred_reward_node: deferred_reward_input_port, + deferred_Input_node: deferred_Input_input_port} + assert comp.controller.state_feature_values == {deferred_reward_node: deferred_reward_input_port, + deferred_Input_node: deferred_Input_input_port} else: assert False, f"TEST ERROR: unrecognized option '{state_features_arg}'" @@ -239,9 +248,10 @@ def test_deferred_init(self, control_spec, state_features_arg): comp.enable_controller = True assert comp.controller.state_features == {'reward[InputPort-0]': 'reward[InputPort-0]', 'Input[InputPort-0]': 'Input[InputPort-0]'} - assert comp.controller.state_feature_values == {reward.input_port: [0.], - Input.input_port: [0.]} + assert comp.controller.state_feature_values == {reward.input_port: [0.], Input.input_port: [0.]} assert all(p.path_afferents for p in comp.controller.state_input_ports) + assert comp.controller.state_input_ports.names == [shadowed_reward_node, shadowed_Input_node] + # comp._analyze_graph() stim_list_dict = { @@ -314,16 +324,12 @@ def test_partial_deferred_init(self, state_features_option): member_node_control_signal = pnl.ControlSignal(control=[(pnl.SLOPE, initial_node_a)], variable=1.0, intensity_cost_function=pnl.Linear(slope=0.0), - allocation_samples=pnl.SampleSpec(start=1.0, - stop=5.0, - num=5)) + allocation_samples=pnl.SampleSpec(start=1.0, stop=5.0, num=5)) deferred_node_control_signal = pnl.ControlSignal(projections=[(pnl.SLOPE, deferred_node)], variable=1.0, intensity_cost_function=pnl.Linear(slope=0.0), - allocation_samples=pnl.SampleSpec(start=1.0, - stop=5.0, - num=5)) + allocation_samples=pnl.SampleSpec(start=1.0, stop=5.0, num=5)) state_features = { 'list': [initial_node_a.input_port, deferred_node.input_port], @@ -350,24 +356,27 @@ def test_partial_deferred_init(self, state_features_option): deferred_node_control_signal ]) ) + deferred_input_port = _deferred_state_feature_spec_msg('deferred[InputPort-0]', 'ocomp') + deferred_node_0 = _deferred_agent_rep_input_port_name('0','ocomp') + deferred_shadowed_0 = _shadowed_state_input_port_name('deferred[InputPort-0]' , deferred_node_0) + deferred_node_deferred = _deferred_agent_rep_input_port_name('deferred[InputPort-0]','ocomp') + shadowed_ia_node = _shadowed_state_input_port_name('ia[InputPort-0]' ,'ia[InputPort-0]') + shadowed_deferred_node_0 = _shadowed_state_input_port_name('deferred[InputPort-0]', deferred_node_0) + shadowed_deferred_node_deferred = _shadowed_state_input_port_name('deferred[InputPort-0]', + 'deferred[InputPort-0]') + + assert ocomp.controller.state_input_ports.names == [shadowed_ia_node, shadowed_deferred_node_0] + if state_features_option in {'list', 'shadow_inputs_dict'}: - assert ocomp.controller.state_features == {'ia[InputPort-0]': - 'ia[InputPort-0]', - 'DEFERRED INPUT NODE 0 OF ocomp': - 'deferred[InputPort-0] NOT (YET) IN ocomp'} + assert ocomp.controller.state_features == {'ia[InputPort-0]': 'ia[InputPort-0]', + deferred_node_0: deferred_input_port} assert ocomp.controller.state_feature_values == {initial_node_a.input_port: [0.], - 'DEFERRED 0 OF ocomp': - 'deferred[InputPort-0] NOT (YET) IN ocomp'} - + deferred_node_0: deferred_input_port} elif state_features_option in {'dict', 'set'}: - assert ocomp.controller.state_features == {'ia[InputPort-0]': - 'ia[InputPort-0]', - 'DEFERRED deferred[InputPort-0] AS INPUT NODE OF ocomp': - 'deferred[InputPort-0] NOT (YET) IN ocomp'} + assert ocomp.controller.state_features == {'ia[InputPort-0]': 'ia[InputPort-0]', + deferred_node_deferred: deferred_input_port} assert ocomp.controller.state_feature_values == {initial_node_a.input_port: [0.], - 'DEFERRED deferred[InputPort-0] OF ocomp': - 'deferred[InputPort-0] NOT (YET) IN ocomp'} - + deferred_node_deferred: deferred_input_port} else: assert False, f"TEST ERROR: unrecognized option '{state_features_option}'" @@ -392,16 +401,17 @@ def test_partial_deferred_init(self, state_features_option): ocomp.add_linear_processing_pathway([deferred_node, initial_node_b]) assert ocomp.controller.state_features == {'ia[InputPort-0]': 'ia[InputPort-0]', 'deferred[InputPort-0]': 'deferred[InputPort-0]'} - assert ocomp.controller.state_feature_values == {initial_node_a.input_port: [0.], deferred_node.input_port: [0.]} assert all(p.path_afferents for p in ocomp.controller.state_input_ports) + assert ocomp.controller.state_input_ports.names == [shadowed_ia_node, shadowed_deferred_node_deferred] result = ocomp.run({ initial_node_a: [1], deferred_node: [1] }) - # result = 10, the sum of the input (1) multiplied by the value of the ControlSignals projecting, respectively, to Node "ia" and Node "deferred_node" + # result = 10, the sum of the input (1) multiplied by the value of the ControlSignals projecting, + # respectively, to Node "ia" and Node "deferred_node" # Control Signal "ia": Maximizes over the search space consisting of ints 1-5 # Control Signal "deferred_node": Maximizes over the search space consisting of ints 1-5 assert result == [[10]] @@ -720,8 +730,9 @@ class TestControlMechanisms: ), ("state_features_test_not_in_agent_rep", "icomp", "A", "I", True, None, pnl.OptimizationControlMechanismError, - '\'OCM\' has \'state_features\' specified ([\'Shadowed input of A[InputPort-0]\']) that are missing ' - 'from both its `agent_rep` (\'INNER COMP\') as well as \'OUTER COMP\' and any Compositions nested within it.' + '\'OCM\' has \'state_features\' specified ([\'SHADOWED INPUT OF A[InputPort-0] FOR I[InputPort-0]\']) ' + 'that are missing from both its `agent_rep` (\'INNER COMP\') as well as \'OUTER COMP\' ' + 'and any Compositions nested within it.' ), ("monitor_for_control_test_not_in_agent_rep", "icomp", "I", "B", True, None, pnl.OptimizationControlMechanismError, @@ -851,12 +862,12 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c f'that is not an INPUT Node of that Composition is not currently supported.\'', # 2 - f'"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'Shadowed input of ' - f'EXT[InputPort-0]\']) that are missing from \'OUTER COMP\' and any Compositions nested within it."', + f'"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'SHADOWED INPUT OF EXT[InputPort-0] ' + f'FOR IA[InputPort-0]\']) that are missing from \'OUTER COMP\' and any Compositions nested within it."', # 3 - f'"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'EXT[OutputPort-0]\']) ' - f'that are missing from \'OUTER COMP\' and any Compositions nested within it."', + '"\'OptimizationControlMechanism-0\' has \'state_features\' specified ([\'INPUT FROM EXT[OutputPort-0] ' + 'FOR IA[InputPort-0]\']) that are missing from \'OUTER COMP\' and any Compositions nested within it."', # 4 f"The '{pnl.STATE_FEATURES}' argument has been specified for 'OptimizationControlMechanism-0' that is using " @@ -910,9 +921,9 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c # 13 f"'OptimizationControlMechanism-0' has '{pnl.STATE_FEATURES}' specified " - f"(['Shadowed input of EXT[InputPort-0]', " - f"'Shadowed input of EXT[InputPort-0]-1', " - f"'Shadowed input of EXT[InputPort-0]-2']) " + f"(['SHADOWED INPUT OF EXT[InputPort-0] FOR IA[InputPort-0]', " + f"'SHADOWED INPUT OF EXT[InputPort-0] FOR OA[InputPort-0]', " + f"'SHADOWED INPUT OF EXT[InputPort-0] FOR OB[InputPort-0]']) " f"that are missing from 'OUTER COMP' and any Compositions nested within it." ] state_feature_args = [ @@ -1011,6 +1022,15 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg monitor_for_control = [ic] if obj_mech == 'mtr_for_ctl' else None # Needs to be a single item for GridSearch state_features = state_features_dict[test_condition] + ia_node = _state_input_port_name('OA[OutputPort-0]', 'IA[InputPort-0]') + oa_node = _state_input_port_name('OA[OutputPort-0]', 'OA[InputPort-0]') + ob_node = _state_input_port_name('OB[OutputPort-0]', 'OB[InputPort-0]') + numeric_ob = _numeric_state_input_port_name('OB[InputPort-0]') + shadowed_ia_node = _shadowed_state_input_port_name('IA[InputPort-0]', 'IA[InputPort-0]') + shadowed_oa_node = _shadowed_state_input_port_name('OA[InputPort-0]', 'OA[InputPort-0]') + shadowed_oa_oc = _shadowed_state_input_port_name('OC[InputPort-0]', 'OA[InputPort-0]') + shadowed_ob_node = _shadowed_state_input_port_name('OB[InputPort-0]', 'OB[InputPort-0]') + if test_condition == 'no_specs': ocm = pnl.OptimizationControlMechanism(objective_mechanism=objective_mechanism, monitor_for_control=monitor_for_control, @@ -1042,11 +1062,13 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg 'OB[InputPort-0]': None} assert ocm.state_feature_values == {} - if test_condition == 'single_shadow_spec': + if test_condition in {'single_shadow_spec', + 'set_spec', + 'set_spec_port', + 'shadow_inputs_dict_spec' + 'no_specs'}: assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OA[InputPort-0]', - 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_input_ports.names == [shadowed_ia_node, shadowed_oa_node, shadowed_ob_node] assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', 'OA[InputPort-0]': 'OA[InputPort-0]', 'OB[InputPort-0]': 'OB[InputPort-0]'} @@ -1055,9 +1077,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ob.input_port: [0., 0., 0.]} if test_condition == 'single_tuple_shadow_spec': - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OA[InputPort-0]', - 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_input_ports.names == [shadowed_ia_node, shadowed_oa_node, shadowed_ob_node] assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', 'OA[InputPort-0]': 'OA[InputPort-0]', 'OB[InputPort-0]': 'OB[InputPort-0]'} @@ -1068,9 +1088,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg if test_condition == 'full_list_spec': assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'OA[OutputPort-0]', - 'OB[InputPort-0] DEFAULT_VARIABLE'] + assert ocm.state_input_ports.names == [shadowed_ia_node, oa_node, numeric_ob] assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', 'OA[InputPort-0]': 'OA[OutputPort-0]', 'OB[InputPort-0]': [3, 1, 2]} @@ -1080,97 +1098,44 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg if test_condition == 'list_spec_with_none': assert len(ocm.state_input_ports) == 2 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'OB[InputPort-0] DEFAULT_VARIABLE'] + assert ocm.state_input_ports.names == [shadowed_ia_node, numeric_ob] assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', 'OA[InputPort-0]': None, 'OB[InputPort-0]': [3, 1, 2]} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), - [[0.], [3, 1, 2]])) - - elif test_condition == 'input_dict_spec': - assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OC[InputPort-0]', - 'OB[OutputPort-0]'] - # 'input_dict_spec': {oa:oc.input_port, icomp:ia, ob:ob.output_port}, # Note: out of order is OK - assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', - 'OA[InputPort-0]': 'OC[InputPort-0]', - 'OB[InputPort-0]': 'OB[OutputPort-0]'} - assert all(np.allclose(expected, actual) - for expected, actual in zip(list(ocm.state_feature_values.values()), - [[0.], [0.], [0, 0, 0]])) + [[0.], [3, 1, 2]])) - elif test_condition == 'input_dict_spec_short': + elif test_condition in {'input_dict_spec', 'input_dict_spec_short'}: assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OC[InputPort-0]', - 'OB[OutputPort-0]'] + assert ocm.state_input_ports.names == [shadowed_ia_node, shadowed_oa_oc, ob_node] assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', 'OA[InputPort-0]': 'OC[InputPort-0]', 'OB[InputPort-0]': 'OB[OutputPort-0]'} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), - [[0.], [0.], [0, 0, 0]])) + [[0.], [0.], [0, 0, 0]])) elif test_condition == 'set_spec_short': assert len(ocm.state_input_ports) == 1 - assert ocm.state_input_ports.names == ['Shadowed input of OA[InputPort-0]'] + assert ocm.state_input_ports.names == [shadowed_oa_node] # 'set_spec': {ob, icomp, oa}, # Note: out of order is OK assert ocm.state_features == {'IA[InputPort-0]': None, 'OA[InputPort-0]': 'OA[InputPort-0]', 'OB[InputPort-0]': None} assert all(np.allclose(expected, actual) for expected, actual in zip(list(ocm.state_feature_values.values()), - [[0.], [0.], [0, 0, 0]])) - - elif test_condition in {'set_spec', 'set_spec_port'}: - assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OA[InputPort-0]', - 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', - 'OA[InputPort-0]': 'OA[InputPort-0]', - 'OB[InputPort-0]': 'OB[InputPort-0]'} - assert all(np.allclose(expected, actual) - for expected, actual in zip(list(ocm.state_feature_values.values()), - [[0.], [0.], [0, 0, 0]])) - - elif test_condition == 'no_specs': - assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OA[InputPort-0]', - 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', - 'OA[InputPort-0]': 'OA[InputPort-0]', - 'OB[InputPort-0]': 'OB[InputPort-0]'} - assert all(np.allclose(expected, actual) - for expected, actual in zip(list(ocm.state_feature_values.values()), - [[0.], [0.], [0, 0, 0]])) - - elif test_condition == 'shadow_inputs_dict_spec': - assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OA[InputPort-0]', - 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', - 'OA[InputPort-0]': 'OA[InputPort-0]', - 'OB[InputPort-0]': 'OB[InputPort-0]'} - assert all(np.allclose(expected, actual) - for expected, actual in zip(list(ocm.state_feature_values.values()), - [[0.], [0.], [0, 0, 0]])) - - elif test_condition == 'shadow_inputs_dict_spec_w_none': - assert len(ocm.state_input_ports) == 2 - assert ocm.state_input_ports.names == ['Shadowed input of IA[InputPort-0]', - 'Shadowed input of OB[InputPort-0]'] - assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', - 'OA[InputPort-0]': None, - 'OB[InputPort-0]': 'OB[InputPort-0]'} - assert all(np.allclose(expected, actual) - for expected, actual in zip(list(ocm.state_feature_values.values()), - [[0.], [0.], [0, 0, 0]])) + [[0.], [0.], [0, 0, 0]])) + + elif test_condition == 'shadow_inputs_dict_spec_w_none': + assert len(ocm.state_input_ports) == 2 + assert ocm.state_input_ports.names == [shadowed_ia_node, shadowed_ob_node] + assert ocm.state_features == {'IA[InputPort-0]': 'IA[InputPort-0]', + 'OA[InputPort-0]': None, + 'OB[InputPort-0]': 'OB[InputPort-0]'} + assert all(np.allclose(expected, actual) + for expected, actual in zip(list(ocm.state_feature_values.values()), + [[0.], [0.], [0, 0, 0]])) elif exception_type is UserWarning: # These also produce errors, tested below @@ -1186,9 +1151,7 @@ def test_ocm_state_feature_specs_and_warnings_and_errors(self, state_feature_arg ocomp.run() if test_condition == 'partial_legal_list_spec': assert len(ocm.state_input_ports) == 3 - assert ocm.state_input_ports.names == ['OA[OutputPort-0]', - 'Shadowed input of OA[InputPort-0]', - 'Shadowed input of OB[InputPort-0]'] + assert ocm.state_input_ports.names == [ia_node, shadowed_oa_node, shadowed_ob_node] # Note: oa is assigned to icomp due to ordering: assert ocm.state_features == {'IA[InputPort-0]': 'OA[OutputPort-0]', 'OA[InputPort-0]': 'OA[InputPort-0]', @@ -1251,13 +1214,13 @@ def test_state_features_in_nested_composition_as_agent_rep(self, nested_agent_re # Test args: if nested_agent_rep is True: agent_rep = mcomp - error_text = f"'OCM' has '{STATE_FEATURES}' specified (['D[OutputPort-0]']) that are missing from both " \ + error_text = f"'OCM' has '{pnl.STATE_FEATURES}' specified (['D[OutputPort-0]']) that are missing from both " \ f"its `agent_rep` ('{nested_agent_rep[1]}') as well as 'OUTER COMP' and any " \ f"Compositions nested within it." else: agent_rep = None - error_text = '"\'OCM\' has \'state_features\' specified ([\'D[OutputPort-0]\']) that are ' \ - 'missing from \'OUTER COMP\' and any Compositions nested within it."' + error_text = f"'OCM' has '{pnl.STATE_FEATURES}' specified (['INPUT FROM D[OutputPort-0] FOR A[SAMPLE]']) " \ + f"that are missing from 'OUTER COMP' and any Compositions nested within it." state_features = { 'single_numeric_spec': [3], @@ -2197,18 +2160,17 @@ def test_add_node_with_controller_spec_and_control_mech_but_not_a_controller(sel ctl = pnl.ControlMechanism(name='CONTROL MECHANISM') warning_msg_1 = '"OutputPort (\'ControlSignal-0\') of \'CONTROL MECHANISM\' doesn\'t have any efferent ' \ 'Projections in \'COMPOSITION\'."' - warning_msg_4 = '"\\nThe following Projections were specified but are not being used by Nodes in ' \ - '\'COMPOSITION\':\\n\\tControlProjection for MECH[slope]"' - warning_msg_5 = '"The \'slope\' parameter of \'MECH\' is specified for control, but the Composition it is in ' \ + warning_msg_2 = '"The \'slope\' parameter of \'MECH\' is specified for control, but the Composition it is in ' \ '(\'COMPOSITION\') does not have a controller; if a controller is not added to COMPOSITION ' \ 'the control specification will be ignored."' + warning_msg_3 = '"\\nThe following Projections were specified but are not being used by Nodes in ' \ + '\'COMPOSITION\':\\n\\tControlProjection for MECH[slope]"' with pytest.warns(UserWarning) as warning: comp = pnl.Composition(name='COMPOSITION', pathways=[ctl]) comp.add_node(mech) comp.run() - assert repr(warning[1].message.args[0]) == warning_msg_1 - assert repr(warning[4].message.args[0]) == warning_msg_4 - assert repr(warning[5].message.args[0]) == warning_msg_5 + assert all(msg in [repr(w.message.args[0]) for w in warning] + for msg in {warning_msg_1, warning_msg_2, warning_msg_3}) @pytest.mark.control @pytest.mark.composition diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index 828468b5151..a5a07507c74 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -246,7 +246,7 @@ def test_converging_pathways(self): ), ( {'show_controller': True, 'show_node_structure': True}, - '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 style=solid]\n\t"my ocm":"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"my ocm":"OutputPort-ib[slope] ControlSignal" -> ib:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> "my ocm":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + '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 style=solid]\n\t"my ocm":"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"my ocm":"OutputPort-ib[slope] ControlSignal" -> ib:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> "my ocm":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ) ] @@ -399,19 +399,19 @@ def test_nested_learning(self, show_graph_kwargs, expected_output): ), ( {'show_nested': False, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ] @@ -510,12 +510,12 @@ def test_nested_learning_test_with_user_specified_target_in_outer_composition( '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> 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}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', - '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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ob[InputPort-0]Shadowed input of oa[InputPort-0]
> 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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}' + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]
> 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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}' ] @pytest.mark.parametrize( @@ -564,12 +564,12 @@ def test_of_show_nested_show_cim_and_show_node_structure( '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', '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\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> midcomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT_CIM" -> ma [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\ticomp -> "midcomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"midcomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "midcomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT_CIM" -> ma [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"icomp OUTPUT_CIM" -> "midcomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> icomp [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', - '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_CIM":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"midcomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> "midcomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> icomp [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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_CIM":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"midcomp OUTPUT_CIM":"OutputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "midcomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> 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_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp INPUT_CIM" [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_CIM":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> "icomp PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t\t"midcomp OUTPUT_CIM" [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_CIM":"OutputPort-OUTPUT_CIM_ia_RESULT" -> "midcomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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_CIM" [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_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', ] @pytest.mark.parametrize( @@ -623,11 +623,11 @@ def test_of_show_3_level_nested_show_cim_and_show_node_structure( '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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}', '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\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', '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_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tctl_mech -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=blue penwidth=1 style=solid]\n\t"icomp OUTPUT_CIM" -> oc [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT_CIM" -> oa [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ob [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob -> "ocomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> "icomp PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM" -> ocm [label="" arrowhead=normal 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_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" -> ia [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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}', - '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-Shadowed input of oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-Shadowed input of ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of oa[InputPort-0]Shadowed input of ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> 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}', + '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 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> 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}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + '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_CIM" [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_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"ocomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT_CIM":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_oa_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"ocomp INPUT_CIM":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ocm:"InputPort-SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]" [label="" arrowhead=normal 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
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF oa[InputPort-0] FOR oa[InputPort-0]SHADOWED INPUT OF ob[InputPort-0] FOR ob[InputPort-0]
> 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_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM" [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 PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"icomp PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1 style=solid]\n\t\t"icomp OUTPUT_CIM" [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_CIM":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1 style=solid]\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}', # FIX: NEEDS TO BE CORRECTED ONCE BUG IS FIXED (SEE MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c) # '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}' ] @@ -679,13 +679,13 @@ def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - # THE FOLLOWING IS INCORRECT (SEE COMMENTS IN TEST) - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + # # THE FOLLOWING IS INCORRECT (SEE COMMENTS IN TEST) + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', # obj_mech 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\tib -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\tic -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\tib -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', @@ -693,13 +693,13 @@ def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" -> "INNER COMP INPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM" -> "OUTER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0" -> "INNER COMP PARAMETER_CIM" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [color=purple penwidth=1 rank=min shape=oval]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" -> "OptimizationControlMechanism-0" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM" -> "OptimizationControlMechanism-0" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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 [color=green penwidth=3 rank=source shape=oval]\n\t\tib [color=pink penwidth=3 rank=max shape=oval]\n\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [color=pink penwidth=3 rank=max shape=oval]\n\t\tib -> ic [label="" arrowhead=normal color=black penwidth=1]\n\t\tic -> id [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP INPUT_CIM" -> ia [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"INNER COMP PARAMETER_CIM" -> ia [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tib -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid -> "INNER COMP OUTPUT_CIM" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [color=red penwidth=3 rank=max shape=oval]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', # THE FOLLOWING IS INCORRECT (SEE COMMENTS IN TEST) - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', - 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-Shadowed input of ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMEShadowed input of ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\tic:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\tib:"OutputPort-OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"INNER COMP" [color=brown penwidth=3 rank=same shape=rectangle]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', + 'digraph "OUTER COMP" {\n\tgraph [label="OUTER COMP" overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\t"OUTER COMP INPUT_CIM" [label=<
INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
OUTER COMP Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "INNER COMP INPUT_CIM":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OUTER COMP OUTPUT_CIM" [label=<
Mechanism:
OUTER COMP Output_CIM
InputPorts
OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=pink penwidth=1 style=solid]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_id_OutputPort-0" -> "OUTER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER COMP_OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0":"OutputPort-ia[slope] ControlSignal" -> "INNER COMP PARAMETER_CIM":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=purple penwidth=1 style=solid]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism" [label=<
OUTCOME
OutputPorts
Mechanism:
OptimizationControlMechanism-0_ObjectiveMechanism
ParameterPorts
offset
scale
InputPorts
Value of ic [OutputPort-0]Value of ib [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OptimizationControlMechanism-0_ObjectiveMechanism":"OutputPort-OUTCOME" -> "OptimizationControlMechanism-0":"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ic_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ic [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"INNER COMP OUTPUT_CIM":"OutputPort-OUTPUT_CIM_ib_OutputPort-0" -> "OptimizationControlMechanism-0_ObjectiveMechanism":"InputPort-Value of ib [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER COMP INPUT_CIM":"OutputPort-INPUT_CIM_INNER COMP_INPUT_CIM_ia_InputPort-0" -> "OptimizationControlMechanism-0":"InputPort-SHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OptimizationControlMechanism-0" [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
OptimizationControlMechanism-0
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF ia[InputPort-0] FOR ia[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_INNER COMP" {\n\t\tgraph [label="INNER COMP" 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=<
OutputPort-0
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tib [label=<
OutputPort-0
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tia:"OutputPort-OutputPort-0" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic [label=<
OutputPort-0
OutputPorts
Mechanism:
ic
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=pink penwidth=3 rank=max shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> ic:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tic:"OutputPort-OutputPort-0" -> id:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"INNER COMP INPUT_CIM" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
INNER COMP Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP INPUT_CIM":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"INNER COMP PARAMETER_CIM" [label=<
PARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
INNER COMP Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"INNER COMP PARAMETER_CIM":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t\t"INNER COMP OUTPUT_CIM" [label=<
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
OutputPorts
Mechanism:
INNER COMP Output_CIM
InputPorts
OUTPUT_CIM_ib_OutputPort-0OUTPUT_CIM_ic_OutputPort-0OUTPUT_CIM_id_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tib:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ib_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tic:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_ic_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid:"OutputPort-OutputPort-0" -> "INNER COMP OUTPUT_CIM":"InputPort-OUTPUT_CIM_id_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tid [label=<
OutputPort-0
OutputPorts
Mechanism:
id
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tcolor=brown\n\t\tlabel="INNER COMP"\n\t}\n}', ] num_show_graph_combos = len(_nested_show_graph_kwargs) obj_mech = ['monitor_for_control'] * num_show_graph_combos + ['obj_mech'] * num_show_graph_combos From 69d09a8a8f57ca48cd27f836a4f84f04d2bbc460 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Sun, 27 Mar 2022 21:36:35 -0400 Subject: [PATCH 262/285] Refactor/composition/ validate input keys (#2361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • test_composition.py implemented test_shadow_nested_nodes that tests shadowing of nested nodes * - * - * - * - * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * - * - * - * - * - * - * - * - * • inputport.py: - add figure for shadowing * - * - * • processingmechanism.py: - __init__(): modify typecheck for input_ports to allow single items * • optimizationcontrolmechanism.py - allow single spec (None, array, tuple, or Components) that is assigned to all INPUT Node InputPorts - state_feature_default is assigned to all unspecified INPUT Node InputPorts (for a list that is shorter than the number, a dict or a set that has fewer, or any that are added to agent_rep after controller is initially constructed and added to Composition) * - * - * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * - * - * - * - * - * • optimizationcontrolmechanism.py: - _state_feature_values_getter(): for numeric state_feature, return state_input_port.functio(numeric_value) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - added tests for single state_feature spec * - composition.py, inputport.py, optimizationcontrolmechanism.py: docstring mods re: "external InputPort" * - * - * - * • test_control.py: - test_state_features_in_nested_composition_as_agent_rep(): - add tests for single state_feature specs - add tests for INPUT Node with more than on InputPort * - * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * • optimizationcontrolmechanism.py: IN PROGRESS - _specified_INPUT_Node_InputPorts_in_order: use name (str) of items not yet in agent_rep rather than 'None' - state_features & state_feature_values: - use name (str) as key if INPUT Node InputPort is not yet in agent_rep - use name (str) if source (spec) is not yet in Composition * - * • optimizationcontrolmechanism.py: IMPLEMENTED - state_features: - convert all keys and values to port.full_name - if not yet in comp or agent_rep, add "DEFERRED..." * • optimizationcontrolmechanism.py: PASSES deferred_init and partial_deferred_init tests * • optimizationcontrolmechanism.py: - state_feature_values: strings for missing keys or values * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_ocm_state_feature_specs_and_warnings_and_errors * • test_control.py: PASSING all state_feature tests * • composition.py: add _is_in_composition method * - * - * - * - * - * - * - * - * - * • optimzationcontrolmechanism.py: fix figure * • optimzationcontrolmechanism.py: fix figure * • optimizationcontrolmechanism.py: - fix bug in which state_feature_default = None was being ignored - fix bug in which deferred nodes were not being assigned state_feature_default * - * • optimizationcontrolmechanism.py: - fix bug in which state_feature_default = None was being ignored - fix bug in which deferred nodes were not being assigned state_feature_default * • test_control.py: - test_deferred_init, test_partial_deferred_init(): added tests for controller.state_input_ports path_afferents * - * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: - fixed bug in handling of lists of numeric specs - renamed state_input_ports for numeric specs * • registry.py: - rename_instance_in_registry(): finish implementing (including **new_name** arg) • optimizationcontrolmechanism.py: - _update_state_features_dict: rename deferred state_input_ports * • registry.py: - rename_instance_in_registry(): finish implementing (including **new_name** arg) • optimizationcontrolmechanism.py: - _update_state_features_dict: rename deferred state_input_ports * - * • optimizationcontrolmechanism.py, test_control.py - standarize prefixes for state_input_ports * • optimizationcontrolmechanism.py, test_control.py - standarize prefixes for state_input_ports * - * - * • test_show_graph remaining * • mechanism.py: - _show_structure(): fix parameterport label bug * - * - * - * - * • composition.py: _validate_input_keys(): add method to validate keys of inputs to run() method * • composition.py: - _validate_input_keys(): add method to validate keys of inputs to run() method - _get_input_receivers(): fix bug concerning inputs count * - * - Co-authored-by: jdcpni --- docs/source/Composition.rst | 1 + .../control/optimizationcontrolmechanism.py | 36 ++ psyneulink/core/components/ports/port.py | 5 +- psyneulink/core/compositions/composition.py | 401 ++++++++++-------- 4 files changed, 265 insertions(+), 178 deletions(-) diff --git a/docs/source/Composition.rst b/docs/source/Composition.rst index 279f431bbb1..116e05881fe 100644 --- a/docs/source/Composition.rst +++ b/docs/source/Composition.rst @@ -25,6 +25,7 @@ Composition Pathway CompositionInterfaceMechanism + OptimizationControlMechanism Scheduling Visualization Report diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 071495fe883..35ed4c778ba 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -621,6 +621,42 @@ `state ` attribute, and `state_dict ` contains the Components associated with each value of `state `. +COMMENT: + > Attributes that pertain to the state of the agent_rep for a given evaluation: + state_feature_specs: a list of the sources for state_feature_values, one for each InputPort of each INPUT Node of the agent_rep + (at all levels of nesting); None for any sources not specified in **state_features** + (corresponding InputPorts are are assigned their default input when agent_rep.evaluate() is executed). + state_feature_values: a dict with entries for each item specified in **state_features** arg of constructor, + in which the key of each entry is an InputPort of an INPUT Node of agent_rep (at any level of nesting) + and the value is the current value of the corresponding state_input_port; the dict is suitable for + use as the **predicted_inputs** or **feature_values** arg of the agent_rep's evaluate() method + (depending on whether the agent_rep is a Composition or a CFA); + note: there are not entries for InputPorts of INPUT Nodes that are not specified in **state_features**; + those are assigned either their default input values (LINK XXX) or the shadowed input of + the corresponding INPUT Node InputPort (LINK XXX), depending on how **state_features** was formatted; + (see LINK XXX for details of formatting). + state_features: a dict with entries corresponding to each item of state_feature_specs, + the keys of which are InputPorts of the INPUT Nodes of the agent_rep, + and values of which are the corresponding state_feature_specs + (i.e., sources of input for those InputPorts when evaluate() is called); + control_allocation: a list of the current values of the OCM's control_signals that are used to modulate the Parameters + specified for control when the agent_rep's evaluate() method is called; + state: a list of the values of the current state, starting with state_feature_values and ending with + control_allocations + state_dict: a dictionary with entries for each state_feature and ControlSignal, keys?? values?? + their source/destination, and their current values + > Constituents of state specifications: + - agent_rep_input_port: an InputPort of an INPUT Node of the agent_rep, + that will receive a value from state_feature_values passed to agent_rep.evaluate() + - source: the source of the input to an agent_rep_input_port, + that sends a Projection to the corresponding state_input_port + + > Relationship of numeric spec to ignoring it (i.e. assigning it None): + allows specification of value as input *just* for simulations (i.e., agent_rep_evaluate) + and not normal execution of comp + +COMMENT + .. _OptimizationControlMechanism_Input: *Input* diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index ed2667b6bba..cdc89dc7b0b 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -2328,9 +2328,8 @@ def _get_input_struct_type(self, ctx): # Check that either all inputs or none are delivered by projections. if len_path_afferents > 0: assert len(func_input_type) == len_path_afferents, \ - "{} shape mismatch: {}\nport:\n\t{}\n\tfunc: {}\npath_afferents: {}".format( - self, func_input_type, self.defaults.variable, - self.function.defaults.variable, len(self.path_afferents)) + f"{self.name} shape mismatch: {func_input_type}\nport:\n\t{self.defaults.variable}" \ + f"\n\tfunc: {self.function.defaults.variable}\npath_afferents: {len(self.path_afferents)}." if len(self.mod_afferents) == 0: # Not need to wrap inputs of non-modulated ports inside mechanisms diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 270d1de5d9a..01c8b69ff29 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -4351,13 +4351,13 @@ def _get_input_receivers(self, - ALL, include the nested Composition AND its INPUT Nodes """ - # FIX: 3/16/22: - # CAN THIS BE REPLACED BY: + # FIX: 3/16/22 - CAN THIS BE REPLACED BY: # return [self._get_destination(output_port.efferents[0])[0] # for _,output_port in self.input_CIM.port_map.values()] assert not (type == PORT and comp_as_node), f"PROGRAM ERROR: _get_input_receivers() can't be called " \ f"for 'ports' and 'nodes' at the same time." + comp = comp or self input_items = [] _update_cim = False @@ -4371,18 +4371,18 @@ def _get_input_receivers(self, include_roles=NodeRole.INPUT) if _input_nodes: for node in _input_nodes: - input_items.extend([input_port for input_port in node.input_ports if not input_port.internal_only]) - # Insure correct number of InputPorts have been identified (i.e., number of InputPorts on comp's input_CIM) - # MODIFIED 3/12/22 NEW: + # Exclude internal_only and shadowers of input (since the latter get inputs for the shadowed item) + input_items.extend([input_port for input_port in node.input_ports + if not (input_port.internal_only or input_port.shadow_inputs)]) + # Ensure correct number of InputPorts have been identified + # (i.e., number of InputPorts on comp's input_CIM) if _update_cim: context = Context() self._determine_pathway_roles(context=context) self._determine_pathway_roles(context) self._create_CIM_ports(context) _update_cim = False - # MODIFIED 3/12/22 OLD: assert len(input_items) == len(comp.input_CIM_ports) - # MODIFIED 3/12/22 END else: # Return all INPUT Nodes _input_nodes = comp.get_nodes_by_role(NodeRole.INPUT) @@ -8669,137 +8669,30 @@ def _validate_single_input(self, receiver, input): _input = None return _input - def _validate_input_shapes(self, inputs): + def _parse_input_dict(self, inputs, context=None): """ - Validates that all inputs provided in input dict are valid + Validate and parse a dict provided as input to a Composition into a standardized form to be used throughout + its execution Returns ------- - `dict` : - The input dict, with shapes corrected if necessary. - - """ - # Loop over all dictionary entries to validate their content and adjust any convenience notations: - - # (1) Replace any user provided convenience notations with values that match the following specs: - # a - all dictionary values are lists containing an input value for each trial (even if only one trial) - # b - each input value is a 2d array that matches variable - # example: { Mech1: [Fully_specified_input_for_mech1_on_trial_1, Fully_specified_input_for_mech1_on_trial_2 … ], - # Mech2: [Fully_specified_input_for_mech2_on_trial_1, Fully_specified_input_for_mech2_on_trial_2 … ]} - # (2) Verify that all nodes provide the same number of inputs (check length of each dictionary value) - _inputs = {} - input_lengths = set() - inputs_to_duplicate = [] - # loop through input dict - for receiver, stimulus in inputs.items(): - # see if the entire stimulus set provided is a valid input for the receiver (i.e. in the case of a call with a - # single trial of provided input) - _input = self._validate_single_input(receiver, stimulus) - if _input is not None: - _input = [_input] - else: - # if _input is None, it may mean there are multiple trials of input in the stimulus set, - # so in list comprehension below loop through and validate each individual input; - _input = [self._validate_single_input(receiver, single_trial_input) for single_trial_input in stimulus] - # Look for any bad ones (for which _validate_single_input() returned None) and report if found - if any(i is None for i in _input): - if isinstance(receiver, InputPort): - receiver_shape = receiver.default_input_shape - receiver_name = receiver.full_name - elif isinstance(receiver, Mechanism): - receiver_shape = receiver.external_input_shape - receiver_name = receiver.name - elif isinstance(receiver, Composition): - receiver_shape = receiver.input_CIM.external_input_shape - receiver_name = receiver.name - # # MODIFIED 3/12/22 OLD: - # bad_stimulus = np.atleast_1d(np.squeeze(np.array(stimulus[_input.index(None)], dtype=object))) - # correct_stimulus = np.atleast_1d(np.array(receiver_shape[_input.index(None)], dtype=object)) - # err_msg = f"Input stimulus ({bad_stimulus}) for {receiver_name} is incompatible with " \ - # f"the shape of its external input ({correct_stimulus})." - # MODIFIED 3/12/22 NEW: - # FIX: MIS-REPORTS INCOMPATIBLITY AS BEING FOR SHAPE IF NUM TRIALS IS DIFFERENT FOR DIFF PORTS - # SHOULD BE HANDLED SAME AS FOR DIFFERNCE ACROSS NODES (PER BELOW) - receiver_shape = np.atleast_1d(np.squeeze(np.array(receiver_shape, dtype=object))) - bad_stimulus = [stim for stim, _inp in zip(stimulus, _input) if _inp is None] - bad_stimulus = np.atleast_1d(np.squeeze(np.array(bad_stimulus, dtype=object))) - err_msg = f"Input stimulus ({bad_stimulus}) for {receiver_name} is incompatible with " \ - f"the shape of its external input ({receiver_shape})." - # MODIFIED 3/12/22 END - # 8/3/17 CW: I admit the error message implementation here is very hacky; - # but it's at least not a hack for "functionality" but rather a hack for user clarity - if "KWTA" in str(type(receiver)): - err_msg = err_msg + " For KWTA mechanisms, remember to append an array of zeros " \ - "(or other values) to represent the outside stimulus for " \ - "the inhibition InputPort, and for Compositions, put your inputs" - raise RunError(err_msg) - _inputs[receiver] = _input - input_length = len(_input) - if input_length == 1: - inputs_to_duplicate.append(receiver) - # track input lengths. stimulus sets of length 1 can be duplicated to match another stimulus set length. - # there can be at maximum 1 other stimulus set length besides 1. - input_lengths.add(input_length) - if 1 in input_lengths: - input_lengths.remove(1) - if len(input_lengths) > 1: - raise CompositionError(f"The input dictionary for {self.name} contains input specifications of different " - f"lengths ({input_lengths}). The same number of inputs must be provided for each " - f"receiver in a Composition.") - elif len(input_lengths) > 0: - num_trials = list(input_lengths)[0] - for mechanism in inputs_to_duplicate: - # hacky, but need to convert to list to use * syntax to duplicate element - if type(_inputs[mechanism]) == np.ndarray: - _inputs[mechanism] = _inputs[mechanism].tolist() - _inputs[mechanism] *= num_trials - return _inputs - - def _flatten_nested_dicts(self, inputs): - """ - Converts inputs provided in the form of a dict for a nested Composition to a list corresponding to the - Composition's input CIM ports - - Returns - ------- + Parsed and standardized input dict - `dict` : - The input dict, with nested dicts corresponding to nested Compositions converted to lists + `int` : + Number of input sets (i.e., trials' worths of inputs) in dict for each input node in the Composition """ - # Inputs provided for nested compositions in the form of a nested dict need to be converted into a list, - # to be provided to the outer Composition's input port that corresponds to the nested Composition - _inputs = {} - for node, inp in inputs.items(): - if node.componentType == 'Composition' and type(inp) == dict: - # If there are multiple levels of nested dicts, we need to convert them starting from the deepest level, - # so recurse down the chain here - inp, num_trials = node._parse_input_dict(inp) - translated_stimulus_dict = {} - - # first time through the stimulus dictionary, assemble a dictionary in which the keys are input CIM - # InputPorts and the values are lists containing the first input value - for nested_input_node, values in inp.items(): - first_value = values[0] - for i in range(len(first_value)): - input_port = nested_input_node.external_input_ports[i] - input_cim_input_port = node.input_CIM_ports[input_port][0] - translated_stimulus_dict[input_cim_input_port] = [first_value[i]] - # then loop through the stimulus dictionary again for each remaining trial - for trial in range(1, num_trials): - translated_stimulus_dict[input_cim_input_port].append(values[trial][i]) - - adjusted_stimulus_list = [] - for trial in range(num_trials): - trial_adjusted_stimulus_list = [] - for port in node.external_input_ports: - trial_adjusted_stimulus_list.append(translated_stimulus_dict[port][trial]) - adjusted_stimulus_list.append(trial_adjusted_stimulus_list) - _inputs[node] = adjusted_stimulus_list - else: - _inputs.update({node:inp}) - return _inputs + # parse a user-provided input dict to format it properly for execution. + # compute number of input sets and return that as well + _inputs = self._parse_names_in_inputs(inputs) + _inputs = self._parse_labels(_inputs) + self._validate_input_dict_keys(_inputs) + _inputs = self._instantiate_input_dict(_inputs) + _inputs = self._flatten_nested_dicts(_inputs) + _inputs = self._validate_input_shapes(_inputs) + num_inputs_sets = len(next(iter(_inputs.values()),[])) + return _inputs, num_inputs_sets def _parse_names_in_inputs(self, inputs): names = [] @@ -8901,59 +8794,85 @@ def _parse_labels(self, inputs, mech=None, port=None, context=None): return _inputs - def _parse_input_dict(self, inputs, context=None): + def _validate_input_dict_keys(self, inputs): + """Validate that keys of inputs are all legal: + - they are all InputPorts, Mechanisms or Compositions; + - they are all (or InputPorts of) INPUT Nodes of Composition at any level of nesting; + - an InputPort and the Mechanism to which it belongs are not *both* specified; + - an InputPort of an input_CIM and the Composition to which it belongs are not *both* specified; + - an InputPort or Mechanism and any Composition under which it is nested are not *both* specified. """ - Validate and parse a dict provided as input to a Composition into a standardized form to be used throughout - its execution - Returns - ------- - `dict` : - Parsed and standardized input dict - - `int` : - Number of input sets (i.e., trials' worths of inputs) in dict for each input node in the Composition + # Validate that keys for inputs are all legal *types* + bad_entries = [key for key in inputs if not isinstance(key, (InputPort, Mechanism, Composition))] + if bad_entries: + bad_entry_names = [repr(key.full_name) if isinstance(key, Port) else repr(key.name) for key in bad_entries] + raise RunError(f"The following items specified in the 'inputs' arg of the run() method for " + f"'{self.name}' that are not a Mechanism, Composition, or an InputPort of one: " + f"{', '.join(bad_entry_names)}.") + + # Validate that keys for inputs all are or belong to *INPUT Nodes* of Composition (at any level of nesting) + all_allowable_entries = self._get_input_receivers(type=PORT) \ + + self._get_input_receivers(type=NODE, comp_as_node=ALL) + bad_entries = [key for key in inputs if key not in all_allowable_entries] + if bad_entries: + bad_entry_names = [repr(key.full_name) if isinstance(key, Port) else repr(key.name) for key in bad_entries] + raise RunError(f"The following items specified in the 'inputs' arg of the run() method for '{self.name}' " + f"are not INPUT Nodes of that Composition (nor InputPorts of them): " + f"{', '.join(bad_entry_names)}.") - """ - # parse a user-provided input dict to format it properly for execution. - # compute number of input sets and return that as well - _inputs = self._parse_names_in_inputs(inputs) - _inputs = self._parse_labels(_inputs) - _inputs = self._instantiate_input_dict(_inputs) - _inputs = self._flatten_nested_dicts(_inputs) - _inputs = self._validate_input_shapes(_inputs) - num_inputs_sets = len(next(iter(_inputs.values()),[])) - return _inputs, num_inputs_sets + # Validate that an InputPort *and* its owner are not *both* specified in inputs + bad_entries = [key.full_name for key in inputs if isinstance(key, InputPort) and key.owner in inputs] + if bad_entries: + raise RunError(f"The 'inputs' arg of the run() method for '{self.name}' includes specifications of the " + f"following InputPorts *and* the Mechanisms to which they belong; only one or the other " + f"can be specified as inputs to run(): {', '.join(bad_entries)}.") + + # Validate that an InputPort of an input_CIM *and* its Composition are not *both* specified in inputs + # (this is unlikely but possible) + bad_entries = [key.full_name for key in inputs + if (isinstance(key, InputPort) + and isinstance(key.owner, CompositionInterfaceMechanism) + and key.owner.composition in inputs)] + if bad_entries: + raise RunError(f"The 'inputs' arg of the run() method for '{self.name}' includes specifications of the " + f"following InputPort(s) of a CompositionInterfaceMechanism *and* the Composition to which " + f"they belong; only one or the other can be specified as inputs to run(): " + f"{', '.join(bad_entries)}.") + + # # Validate that InputPort or Mechanism and the Composition(s) under which it is nested are not both specified + def check_for_items_in_nested_comp(comp): + bad_entries = [] + for node in comp._all_nodes: # note: this only includes nodes as top level of comp + if isinstance(node, Composition) and node in inputs: + all_nested_items = node._get_input_receivers(type=PORT) \ + + node._get_input_receivers(type=NODE, comp_as_node=ALL) + bad_entries.extend([(entry, node) for entry in inputs if entry in all_nested_items]) + bad_entries.extend(check_for_items_in_nested_comp(node)) + return bad_entries + bad_entries = check_for_items_in_nested_comp(self) + if bad_entries: + bad_entry_names = [(key.full_name, comp.name) if isinstance(key, Port) else (key.name, comp.name) + for key,comp in bad_entries] + raise RunError(f"The 'inputs' arg of the run() method for '{self.name}' includes specifications of the " + f"following InputPorts or Mechanisms *and* the Composition within which they are nested: " + # f"{', '.join(bad_entry_names)}.") + f"{bad_entry_names}.") def _instantiate_input_dict(self, inputs): - """Implement dict with all INPUT Nodes of Composition as keys and their assigned inputs or defaults as values + """Implement dict with all INPUT Node of Composition as keys and their assigned inputs or defaults as values **inputs** can contain specifications for inputs to InputPorts, Mechanisms and/or nested Compositions, that can be at any level of nesting within self. - Validate that all Nodes included in input dict are INPUT nodes of Composition. Consolidate any entries of **inputs** with InputPorts as keys to Mechanism or Composition entries - If any INPUT nodes of Composition are not included, add them to the input_dict using their default values. - Note: all entries must specify either a single trial's worth of input, or the same number as all others. - - Returns - ------- + If any INPUT Nodes of Composition are not included, add them to the input_dict using their default values. + InputPort entries must specify either a single trial or the same number as all other InPorts for that Node: + - preprocess InputPorts for a Node to determine maximum number of trials specified, and use to set mech_shape + - if more than one trial is specified for any InputPort, assign fillers to ones that specify only one trial + (this does not apply to Mechanism or Composition specifications, as they are tested in validate_input_shapes) - `dict` : - Input dict, with added entries for any input Nodes or Ports for which input was not provided + Return input_dict, with added entries for any INPUT Nodes or InputPorts for which input was not provided """ - # # FIX: 3/12/22 - DOCUMENT PREPROCESSING OF PORTS FOR EACH NODE TO DETERMINE MAXIMUM NUMBER OF TRIALS FOR EACH, - # # TO SET mech_shape FOR BELOW, AND ASSIGN FILLERS FOR ONES THAT ARE SHORT OF THE MAX LENGTH - # # SHOULD ALSO ENFORCE 1 OR SAME NUMBER FOR ALL PORTS, AND FILL IN FOR ONES THAT ARE SHORT OF - # # MAX (NO NEED TO DO SO FOR MECH OR COMP SPECS SINCE THOSE ARE TESTED IN _validate_input_shapes - # # ALSO NO NEED TO WORRY ABOUT DIFFERENCES ACROSS NODES, AS THAT TOO WILL BE TESTED THERE - - # Validate that keys for inputs are all legal entries - bad_entries = [repr(key) for key in inputs - if not isinstance(key, (InputPort, Mechanism, Composition))] - if bad_entries: - assert False, f"One or more entries are specified in the inputs for '{self.name}' that are not " \ - f"a Mechanism, Composition, or an InputPort of one: {', '.join(bad_entries)}." - input_dict = {} input_nodes = self.get_nodes_by_role(NodeRole.INPUT) remaining_inputs = set(inputs) @@ -9053,9 +8972,9 @@ def _instantiate_input_dict(self, inputs): remaining_inputs = remaining_inputs - inputs_to_remove if remaining_inputs: - raise CompositionError(f"The following items specified in the 'inputs' arg of the run() method for " - f"'{self.name}' are not INPUT Nodes of that Composition (nor InputPorts " - f"of them): {remaining_inputs}.") + assert False, f"PROGRAM ERROR: the following items specified in the 'inputs' arg of the run() method " \ + f"for '{self.name}' are not INPUT Nodes of that Composition (nor InputPorts of them): " \ + f"{remaining_inputs} -- SHOULD HAVE RAISED ERROR IN composition._validate_input_dict_keys()" # If any INPUT Nodes of the Composition are not specified, add them and assign default_external_input_values for node in input_nodes: @@ -9064,6 +8983,138 @@ def _instantiate_input_dict(self, inputs): return input_dict + def _flatten_nested_dicts(self, inputs): + """ + Converts inputs provided in the form of a dict for a nested Composition to a list corresponding to the + Composition's input CIM ports + + Returns + ------- + + `dict` : + The input dict, with nested dicts corresponding to nested Compositions converted to lists + + """ + # Inputs provided for nested compositions in the form of a nested dict need to be converted into a list, + # to be provided to the outer Composition's input port that corresponds to the nested Composition + _inputs = {} + for node, inp in inputs.items(): + if node.componentType == 'Composition' and type(inp) == dict: + # If there are multiple levels of nested dicts, we need to convert them starting from the deepest level, + # so recurse down the chain here + inp, num_trials = node._parse_input_dict(inp) + translated_stimulus_dict = {} + + # first time through the stimulus dictionary, assemble a dictionary in which the keys are input CIM + # InputPorts and the values are lists containing the first input value + for nested_input_node, values in inp.items(): + first_value = values[0] + for i in range(len(first_value)): + input_port = nested_input_node.external_input_ports[i] + input_cim_input_port = node.input_CIM_ports[input_port][0] + translated_stimulus_dict[input_cim_input_port] = [first_value[i]] + # then loop through the stimulus dictionary again for each remaining trial + for trial in range(1, num_trials): + translated_stimulus_dict[input_cim_input_port].append(values[trial][i]) + + adjusted_stimulus_list = [] + for trial in range(num_trials): + trial_adjusted_stimulus_list = [] + for port in node.external_input_ports: + trial_adjusted_stimulus_list.append(translated_stimulus_dict[port][trial]) + adjusted_stimulus_list.append(trial_adjusted_stimulus_list) + _inputs[node] = adjusted_stimulus_list + else: + _inputs.update({node:inp}) + return _inputs + + def _validate_input_shapes(self, inputs): + """ + Validates that all inputs provided in input dict are valid + + Returns + ------- + + `dict` : + The input dict, with shapes corrected if necessary. + + """ + # Loop over all dictionary entries to validate their content and adjust any convenience notations: + + # (1) Replace any user provided convenience notations with values that match the following specs: + # a - all dictionary values are lists containing an input value for each trial (even if only one trial) + # b - each input value is a 2d array that matches variable + # example: { Mech1: [Fully_specified_input_for_mech1_on_trial_1, Fully_specified_input_for_mech1_on_trial_2 … ], + # Mech2: [Fully_specified_input_for_mech2_on_trial_1, Fully_specified_input_for_mech2_on_trial_2 … ]} + # (2) Verify that all nodes provide the same number of inputs (check length of each dictionary value) + _inputs = {} + input_lengths = set() + inputs_to_duplicate = [] + # loop through input dict + for receiver, stimulus in inputs.items(): + # see if the entire stimulus set provided is a valid input for the receiver (i.e. in the case of a call with a + # single trial of provided input) + _input = self._validate_single_input(receiver, stimulus) + if _input is not None: + _input = [_input] + else: + # if _input is None, it may mean there are multiple trials of input in the stimulus set, + # so in list comprehension below loop through and validate each individual input; + _input = [self._validate_single_input(receiver, single_trial_input) for single_trial_input in stimulus] + # Look for any bad ones (for which _validate_single_input() returned None) and report if found + if any(i is None for i in _input): + if isinstance(receiver, InputPort): + receiver_shape = receiver.default_input_shape + receiver_name = receiver.full_name + elif isinstance(receiver, Mechanism): + receiver_shape = receiver.external_input_shape + receiver_name = receiver.name + elif isinstance(receiver, Composition): + receiver_shape = receiver.input_CIM.external_input_shape + receiver_name = receiver.name + # # MODIFIED 3/12/22 OLD: + # bad_stimulus = np.atleast_1d(np.squeeze(np.array(stimulus[_input.index(None)], dtype=object))) + # correct_stimulus = np.atleast_1d(np.array(receiver_shape[_input.index(None)], dtype=object)) + # err_msg = f"Input stimulus ({bad_stimulus}) for {receiver_name} is incompatible with " \ + # f"the shape of its external input ({correct_stimulus})." + # MODIFIED 3/12/22 NEW: + # FIX: MIS-REPORTS INCOMPATIBLITY AS BEING FOR SHAPE IF NUM TRIALS IS DIFFERENT FOR DIFF PORTS + # SHOULD BE HANDLED SAME AS FOR DIFFERNCE ACROSS NODES (PER BELOW) + receiver_shape = np.atleast_1d(np.squeeze(np.array(receiver_shape, dtype=object))) + bad_stimulus = [stim for stim, _inp in zip(stimulus, _input) if _inp is None] + bad_stimulus = np.atleast_1d(np.squeeze(np.array(bad_stimulus, dtype=object))) + err_msg = f"Input stimulus ({bad_stimulus}) for {receiver_name} is incompatible with " \ + f"the shape of its external input ({receiver_shape})." + # MODIFIED 3/12/22 END + # 8/3/17 CW: I admit the error message implementation here is very hacky; + # but it's at least not a hack for "functionality" but rather a hack for user clarity + if "KWTA" in str(type(receiver)): + err_msg = err_msg + " For KWTA mechanisms, remember to append an array of zeros " \ + "(or other values) to represent the outside stimulus for " \ + "the inhibition InputPort, and for Compositions, put your inputs" + raise RunError(err_msg) + _inputs[receiver] = _input + input_length = len(_input) + if input_length == 1: + inputs_to_duplicate.append(receiver) + # track input lengths. stimulus sets of length 1 can be duplicated to match another stimulus set length. + # there can be at maximum 1 other stimulus set length besides 1. + input_lengths.add(input_length) + if 1 in input_lengths: + input_lengths.remove(1) + if len(input_lengths) > 1: + raise CompositionError(f"The input dictionary for {self.name} contains input specifications of different " + f"lengths ({input_lengths}). The same number of inputs must be provided for each " + f"receiver in a Composition.") + elif len(input_lengths) > 0: + num_trials = list(input_lengths)[0] + for mechanism in inputs_to_duplicate: + # hacky, but need to convert to list to use * syntax to duplicate element + if type(_inputs[mechanism]) == np.ndarray: + _inputs[mechanism] = _inputs[mechanism].tolist() + _inputs[mechanism] *= num_trials + return _inputs + def _parse_run_inputs(self, inputs, context=None): """ Takes user-provided input for entire run and parses it according to its type From c623b6fd9ecd75e6eea3e1e3e4d074f28d664982 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 27 Mar 2022 22:30:55 -0400 Subject: [PATCH 263/285] OutputPort: Add support for Time parameters in output port variable spec Time indexing is only available in graph-scheduler>=1.1, add explicit check for retrieving the correct. Reduce reliance on exception paths, exceptions are slow and impair readability. Signed-off-by: Jan Vesely --- .../core/components/ports/outputport.py | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/psyneulink/core/components/ports/outputport.py b/psyneulink/core/components/ports/outputport.py index 0094a92e571..5c2be3a09bc 100644 --- a/psyneulink/core/components/ports/outputport.py +++ b/psyneulink/core/components/ports/outputport.py @@ -674,10 +674,7 @@ def parse_variable_spec(spec): return spec elif isinstance(spec, tuple): # Tuple indexing item of owner's attribute (e.g.,: OWNER_VALUE, int)) - try: - owner_param_name = output_port_spec_to_parameter_name[spec[0]] - except KeyError: - owner_param_name = spec[0] + owner_param_name = output_port_spec_to_parameter_name.get(spec[0], spec[0]) try: index = spec[1]() if callable(spec[1]) else spec[1] @@ -685,19 +682,14 @@ def parse_variable_spec(spec): # context is None during initialization, and we don't want to # incur the cost of .get during execution if context is None: - return getattr(owner.parameters, owner_param_name).get(context)[index] - else: - return getattr(owner.parameters, owner_param_name)._get(context)[index] - except TypeError: - if context is None: - if getattr(owner.parameters, owner_param_name).get(context) is None: - return None - elif getattr(owner.parameters, owner_param_name)._get(context) is None: - return None + val = getattr(owner.parameters, owner_param_name).get(context) else: - # raise OutputPortError("Can't parse variable ({}) for {} of {}". - # format(spec, output_port_name or OutputPort.__name__, owner.name)) - raise Exception + val = getattr(owner.parameters, owner_param_name)._get(context) + + if hasattr(val, '_get_by_time_scale'): + return val._get_by_time_scale(index) + + return val if val is None else val[index] except: raise OutputPortError(f"Can't parse variable ({spec}) for " f"{output_port_name or OutputPort.__name__} of {owner.name}.") From 258f7bb8037edf1902a2544c45087b3d0a193743 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 26 Mar 2022 23:14:40 -0400 Subject: [PATCH 264/285] llvm, mechanism: Add support for "num_executions" output port specification Signed-off-by: Jan Vesely --- .../core/components/mechanisms/mechanism.py | 20 ++++++++++++++++--- tests/ports/test_output_ports.py | 6 ++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 7cdcc51d4ac..6f93f01ba1d 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2918,7 +2918,7 @@ def _gen_llvm_ports(self, ctx, builder, ports, group, array_1d = pnlvm.ir.ArrayType(p_input_data.type.pointee, 1) array_2d = pnlvm.ir.ArrayType(array_1d, 1) assert array_1d == p_function.args[2].type.pointee or array_2d == p_function.args[2].type.pointee, \ - "{} vs.{}".format(p_function.args[2].type.pointee, p_input_data.type.pointee) + "{} vs. {}".format(p_function.args[2].type.pointee, p_input_data.type.pointee) p_input = builder.bitcast(p_input_data, p_function.args[2].type) else: @@ -3026,7 +3026,7 @@ def _gen_llvm_output_port_parse_variable(self, ctx, builder, try: name = port_spec[0] - ids = (x() if callable(x) else x for x in port_spec[1:]) + ids = (x() if callable(x) else getattr(x, 'value', x) for x in port_spec[1:]) except TypeError as e: # TypeError means we can't index. # Convert this to assertion failure below @@ -3035,7 +3035,21 @@ def _gen_llvm_output_port_parse_variable(self, ctx, builder, #TODO: support more spec options if name == OWNER_VALUE: data = value - return builder.gep(data, [ctx.int32_ty(0), *(ctx.int32_ty(i) for i in ids)]) + elif name in self.llvm_state_ids: + data = pnlvm.helpers.get_state_ptr(builder, self, mech_state, name) + else: + data = None + + if data is not None: + parsed = builder.gep(data, [ctx.int32_ty(0), *(ctx.int32_ty(i) for i in ids)]) + # "num_executions" are kept as int64, we need to convert the value to float first + if name == "num_executions": + count = builder.load(parsed) + count_fp = builder.uitofp(count, ctx.float_ty) + parsed = builder.alloca(count_fp.type) + builder.store(count_fp, parsed) + + return parsed assert False, "Unsupported OutputPort spec: {} ({})".format(port_spec, value.type) diff --git a/tests/ports/test_output_ports.py b/tests/ports/test_output_ports.py index 3f8bb230402..c4625f17634 100644 --- a/tests/ports/test_output_ports.py +++ b/tests/ports/test_output_ports.py @@ -38,6 +38,12 @@ def test_output_port_variable_spec(self, mech_mode): ((pnl.OWNER_VALUE, 2), [3], [3]), pytest.param((pnl.OWNER_VALUE, 3), [3], [3], marks=[pytest.mark.xfail()]), ((pnl.OWNER_EXECUTION_COUNT), [4], [8]), +# FIXME: LIFE fails in python, RUN fails in LLVM +# (("num_executions", pnl.TimeScale.LIFE), [4], [8]), +# (("num_executions", pnl.TimeScale.RUN), [4], [4]), + (("num_executions", pnl.TimeScale.TRIAL), [2], [2]), + (("num_executions", pnl.TimeScale.PASS), [1], [1]), + (("num_executions", pnl.TimeScale.TIME_STEP), [1], [1]), ], ids=lambda x: str(x) if len(x) != 1 else '') @pytest.mark.usefixtures("comp_mode_no_llvm") def tests_output_port_variable_spec_composition(self, comp_mode, spec, expected1, expected2): From 727cb00a4d06d6830961ef48d5566ecc3aacf1cc Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 26 Mar 2022 23:48:19 -0400 Subject: [PATCH 265/285] llvm, composition/run: Move TimeScale.RUN count zeroying out of the run loop These values should only be zeroed once per 'RUN'. Enable corresponding output port test. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/codegen.py | 14 +++++++------- tests/ports/test_output_ports.py | 8 ++++++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index c09bfebef08..be676e6fca9 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -984,6 +984,13 @@ def gen_composition_run(ctx, composition, *, tags:frozenset): builder.store(data_in.type.pointee(input_init), data_in) builder.store(inputs_ptr.type.pointee(1), inputs_ptr) + # Reset internal 'RUN' clocks of each node + for idx, node in enumerate(composition._all_nodes): + node_state = builder.gep(state, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(idx)]) + num_executions_ptr = helpers.get_state_ptr(builder, node, node_state, "num_executions") + num_exec_time_ptr = builder.gep(num_executions_ptr, [ctx.int32_ty(0), ctx.int32_ty(TimeScale.RUN.value)]) + builder.store(num_exec_time_ptr.type.pointee(0), num_exec_time_ptr) + # Allocate and initialize condition structure cond_gen = helpers.ConditionGenerator(ctx, composition) cond_type = cond_gen.get_condition_struct_type() @@ -1028,13 +1035,6 @@ def gen_composition_run(ctx, composition, *, tags:frozenset): input_idx = builder.urem(iters, builder.load(inputs_ptr)) data_in_ptr = builder.gep(data_in, [input_idx]) - # Reset internal 'RUN' clocks of each node - for idx, node in enumerate(composition._all_nodes): - node_state = builder.gep(state, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(idx)]) - num_executions_ptr = helpers.get_state_ptr(builder, node, node_state, "num_executions") - num_exec_time_ptr = builder.gep(num_executions_ptr, [ctx.int32_ty(0), ctx.int32_ty(TimeScale.RUN.value)]) - builder.store(num_exec_time_ptr.type.pointee(0), num_exec_time_ptr) - # Call execution exec_tags = tags.difference({"run"}) exec_f = ctx.import_llvm_function(composition, tags=exec_tags) diff --git a/tests/ports/test_output_ports.py b/tests/ports/test_output_ports.py index c4625f17634..ba414d3c413 100644 --- a/tests/ports/test_output_ports.py +++ b/tests/ports/test_output_ports.py @@ -38,15 +38,19 @@ def test_output_port_variable_spec(self, mech_mode): ((pnl.OWNER_VALUE, 2), [3], [3]), pytest.param((pnl.OWNER_VALUE, 3), [3], [3], marks=[pytest.mark.xfail()]), ((pnl.OWNER_EXECUTION_COUNT), [4], [8]), -# FIXME: LIFE fails in python, RUN fails in LLVM +# FIXME: LIFE fails in python # (("num_executions", pnl.TimeScale.LIFE), [4], [8]), -# (("num_executions", pnl.TimeScale.RUN), [4], [4]), + (("num_executions", pnl.TimeScale.RUN), [4], [4]), (("num_executions", pnl.TimeScale.TRIAL), [2], [2]), (("num_executions", pnl.TimeScale.PASS), [1], [1]), (("num_executions", pnl.TimeScale.TIME_STEP), [1], [1]), ], ids=lambda x: str(x) if len(x) != 1 else '') @pytest.mark.usefixtures("comp_mode_no_llvm") def tests_output_port_variable_spec_composition(self, comp_mode, spec, expected1, expected2): + if (len(spec) == 2) and (spec[1] == pnl.TimeScale.RUN) and \ + ((comp_mode & pnl.ExecutionMode._Exec) == pnl.ExecutionMode._Exec): + pytest.skip("{} is not supported in {}".format(spec[1], comp_mode)) + # Test specification of OutputPort's variable # OutputPort mech.output_ports['all'] has a different dimensionality than the other OutputPorts; # as a consequence, when added as a terminal node, the Composition can't construct an IDENTITY_MATRIX From b7f34b8fffff30e24177205ee48869ee81b5c6ae Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 27 Mar 2022 05:02:26 -0400 Subject: [PATCH 266/285] component: Update TimeScale.LIFE counter on execution Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 2 +- tests/ports/test_output_ports.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 34a8cb0fbcc..9bb74df70fa 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -3120,7 +3120,7 @@ def _execute(self, variable=None, context=None, runtime_params=None, **kwargs): self._update_current_execution_time(context=context) self._increment_num_executions( context, - [TimeScale.TIME_STEP, TimeScale.PASS, TimeScale.TRIAL, TimeScale.RUN] + [TimeScale.TIME_STEP, TimeScale.PASS, TimeScale.TRIAL, TimeScale.RUN, TimeScale.LIFE] ) value = None diff --git a/tests/ports/test_output_ports.py b/tests/ports/test_output_ports.py index ba414d3c413..71761b860f4 100644 --- a/tests/ports/test_output_ports.py +++ b/tests/ports/test_output_ports.py @@ -38,8 +38,7 @@ def test_output_port_variable_spec(self, mech_mode): ((pnl.OWNER_VALUE, 2), [3], [3]), pytest.param((pnl.OWNER_VALUE, 3), [3], [3], marks=[pytest.mark.xfail()]), ((pnl.OWNER_EXECUTION_COUNT), [4], [8]), -# FIXME: LIFE fails in python -# (("num_executions", pnl.TimeScale.LIFE), [4], [8]), + (("num_executions", pnl.TimeScale.LIFE), [4], [8]), (("num_executions", pnl.TimeScale.RUN), [4], [4]), (("num_executions", pnl.TimeScale.TRIAL), [2], [2]), (("num_executions", pnl.TimeScale.PASS), [1], [1]), From b605825b70e83dafb87bafbc1e56202a9d00cb67 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 27 Mar 2022 14:12:15 -0400 Subject: [PATCH 267/285] llvm, composition/execute: Only zero TIME_STEP counters for nodes that executed in previous time step Signed-off-by: Jan Vesely --- psyneulink/core/llvm/codegen.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index be676e6fca9..7b8258e077b 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -773,6 +773,8 @@ def gen_composition_exec(ctx, composition, *, tags:frozenset): # Reset internal TRIAL/PASS/TIME_STEP clock for each node + # This also resets TIME_STEP counter for input_CIM and parameter_CIM + # executed above for time_loc in num_exec_locs.values(): for scale in (TimeScale.TRIAL, TimeScale.PASS, TimeScale.TIME_STEP): num_exec_time_ptr = builder.gep(time_loc, [ctx.int32_ty(0), @@ -833,6 +835,8 @@ def gen_composition_exec(ctx, composition, *, tags:frozenset): # Generate loop body builder.position_at_end(loop_body) + previous_step = builder.load(run_set_ptr) + zero = ctx.int32_ty(0) any_cond = ctx.bool_ty(0) # Calculate execution set before running the mechanisms @@ -850,12 +854,15 @@ def gen_composition_exec(ctx, composition, *, tags:frozenset): builder.store(node_cond, run_set_node_ptr) # Reset internal TIME_STEP clock for each node - # NOTE: This is done _after_ condition evluation, otherwise + # NOTE: This is done _after_ condition evaluation, otherwise # TIME_STEP related conditions will only see 0 executions - for time_loc in num_exec_locs.values(): - num_exec_time_ptr = builder.gep(time_loc, [ctx.int32_ty(0), - ctx.int32_ty(TimeScale.TIME_STEP.value)]) - builder.store(num_exec_time_ptr.type.pointee(0), num_exec_time_ptr) + for idx, node in enumerate(composition.nodes): + ran_prev_step = builder.extract_value(previous_step, [idx]) + time_loc = num_exec_locs[node] + with builder.if_then(ran_prev_step): + num_exec_time_ptr = builder.gep(time_loc, [ctx.int32_ty(0), + ctx.int32_ty(TimeScale.TIME_STEP.value)]) + builder.store(num_exec_time_ptr.type.pointee(0), num_exec_time_ptr) for idx, node in enumerate(composition.nodes): From e534a5b5a2817f94e7eb9f7f41af7b8b14495e98 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 27 Mar 2022 17:55:36 -0400 Subject: [PATCH 268/285] llvm, mechanism: Update output ports after updating mech state Update mech value and num_executions counts Signed-off-by: Jan Vesely --- psyneulink/core/components/mechanisms/mechanism.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 6f93f01ba1d..97962f1503e 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -3120,8 +3120,6 @@ def _gen_llvm_function_internal(self, ctx, builder, m_params, m_state, arg_in, new_val = builder.add(new_val, new_val.type(1)) builder.store(new_val, num_exec_time_ptr) - builder = self._gen_llvm_output_ports(ctx, builder, value, m_base_params, m_state, arg_in, arg_out) - val_ptr = pnlvm.helpers.get_state_ptr(builder, self, m_state, "value") if val_ptr.type.pointee == value.type.pointee: pnlvm.helpers.push_state_val(builder, self, m_state, "value", value) @@ -3129,6 +3127,9 @@ def _gen_llvm_function_internal(self, ctx, builder, m_params, m_state, arg_in, # FIXME: Does this need some sort of parsing? warnings.warn("Shape mismatch: function result does not match mechanism value param: {} vs. {}".format(value.type.pointee, val_ptr.type.pointee)) + # Run output ports after updating the mech state (num_executions and value) + builder = self._gen_llvm_output_ports(ctx, builder, value, m_base_params, m_state, arg_in, arg_out) + # is_finished should be checked after output ports ran is_finished_f = ctx.import_llvm_function(self, tags=tags.union({"is_finished"})) is_finished_cond = builder.call(is_finished_f, [m_params, m_state, arg_in, From 2313d4fd090581884774a2a6bd1b4481d696e2fb Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 27 Mar 2022 19:44:05 -0400 Subject: [PATCH 269/285] tests/scheduling/Threshold: Use fixture 'comp_mode_no_llvm' instead of manual skip Signed-off-by: Jan Vesely --- tests/scheduling/test_condition.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/scheduling/test_condition.py b/tests/scheduling/test_condition.py index 111829a8d4f..f9684059100 100644 --- a/tests/scheduling/test_condition.py +++ b/tests/scheduling/test_condition.py @@ -703,12 +703,10 @@ def test_AllHaveRun_2(self): ] ) @pytest.mark.parametrize('threshold', [10, 10.0]) + @pytest.mark.usefixtures("comp_mode_no_llvm") def test_Threshold_parameters( self, parameter, indices, default_variable, integration_rate, expected_results, threshold, comp_mode ): - if comp_mode is pnl.ExecutionMode.LLVM: - pytest.skip('ExecutionMode.LLVM does not support Parameter access in conditions') - A = TransferMechanism( default_variable=default_variable, integrator_mode=True, From a9a33be4802ba982ae11359e6c6d04bc06bb2b8f Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 27 Mar 2022 19:44:49 -0400 Subject: [PATCH 270/285] llvm, mechanism: Redirect 'execution_count' -> 'num_executions'[TimeScale.LIFE] Compiled execution si always constrained to a single context. This makes the two parameters identical. Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 3 +-- psyneulink/core/components/mechanisms/mechanism.py | 14 ++++++-------- psyneulink/core/llvm/helpers.py | 7 +++++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 9bb74df70fa..780673f3953 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1325,8 +1325,7 @@ def _get_compilation_state(self): # and need to track executions if hasattr(self, 'ports'): whitelist.update({"value", "num_executions_before_finished", - "num_executions", "is_finished_flag", - "execution_count"}) + "num_executions", "is_finished_flag"}) # Only mechanisms and compositions need 'num_executions' if hasattr(self, 'nodes'): diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 97962f1503e..cbcbab5ebd0 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -3021,8 +3021,11 @@ def _gen_llvm_output_port_parse_variable(self, ctx, builder, if port_spec == OWNER_VALUE: return value elif port_spec == OWNER_EXECUTION_COUNT: - execution_count = pnlvm.helpers.get_state_ptr(builder, self, mech_state, EXECUTION_COUNT) - return execution_count + # Convert execution count to (num_executions, TimeScale.LIFE) + # The difference in Python PNL is that the former counts across + # all contexts. This is not possible in compiled code, thus + # the two are identical. + port_spec = ("num_executions", TimeScale.LIFE) try: name = port_spec[0] @@ -3103,13 +3106,8 @@ def _gen_llvm_function_internal(self, ctx, builder, m_params, m_state, arg_in, m_params, m_state, arg_in, ip_output, tags=tags) - # Update execution counter - exec_count_ptr = pnlvm.helpers.get_state_ptr(builder, self, m_state, "execution_count") - exec_count = builder.load(exec_count_ptr) - exec_count = builder.fadd(exec_count, exec_count.type(1)) - builder.store(exec_count, exec_count_ptr) - # Update internal clock (i.e. num_executions parameter) + # Update num_executions parameter num_executions_ptr = pnlvm.helpers.get_state_ptr(builder, self, m_state, "num_executions") for scale in TimeScale: assert scale.value < len(num_executions_ptr.type.pointee) diff --git a/psyneulink/core/llvm/helpers.py b/psyneulink/core/llvm/helpers.py index b97f77480af..6b54aaf4bde 100644 --- a/psyneulink/core/llvm/helpers.py +++ b/psyneulink/core/llvm/helpers.py @@ -717,6 +717,13 @@ def generate_sched_condition(self, builder, condition, cond_ptr, node, comparator = condition.comparator indices = condition.indices + # Convert execution_count to ('num_executions', TimeScale.LIFE). + # These two are identical in compiled semantics. + if param == 'execution_count': + assert indices is None + param = 'num_executions' + indices = TimeScale.LIFE + assert param in target.llvm_state_ids, ( f"Threshold for {target} only supports items in llvm_state_ids" f" ({target.llvm_state_ids})" From 8f61c3460a428348f9803a06e2191ec062a209e1 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 23 Mar 2022 23:12:48 -0400 Subject: [PATCH 271/285] Projection, ConnectionInfo: allow deactivation/removal of composition --- psyneulink/core/components/projections/projection.py | 9 +++++++++ psyneulink/core/globals/socket.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index cc1a1f81a53..9fa2affcdfd 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -978,6 +978,15 @@ def _activate_for_compositions(self, composition): def _activate_for_all_compositions(self): self._activate_for_compositions(ConnectionInfo.ALL) + def _deactivate_for_compositions(self, composition): + try: + self.receiver.afferents_info[self].remove_composition(composition) + except KeyError: + warnings.warn(f'{self} was not active for {composition}') + + def _deactivate_for_all_compositions(self): + self._deactivate_for_all_compositions(ConnectionInfo.ALL) + def _delete_projection(projection, context=None): """Delete Projection, its entries in receiver and sender Ports, and in ProjectionRegistry""" projection.sender._remove_projection_from_port(projection) diff --git a/psyneulink/core/globals/socket.py b/psyneulink/core/globals/socket.py index e995466f98c..54e93c44a8d 100644 --- a/psyneulink/core/globals/socket.py +++ b/psyneulink/core/globals/socket.py @@ -34,6 +34,15 @@ def add_composition(self, composition): else: self.compositions.add(composition) + def remove_composition(self, composition): + if composition is self.ALL: + self.compositions = set() + else: + try: + self.compositions.remove(composition) + except (AttributeError, KeyError): + logger.info('Attempted to remove composition from {} but was not active'.format(self)) + def is_active_in_composition(self, composition): if self.compositions is None: return False From 0f39242a307f97421acefa5b3a38cddb040650a8 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Thu, 24 Mar 2022 01:33:02 -0400 Subject: [PATCH 272/285] Projection: add is_active_in_composition method --- psyneulink/core/components/projections/projection.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/psyneulink/core/components/projections/projection.py b/psyneulink/core/components/projections/projection.py index 9fa2affcdfd..6999bca6702 100644 --- a/psyneulink/core/components/projections/projection.py +++ b/psyneulink/core/components/projections/projection.py @@ -987,6 +987,9 @@ def _deactivate_for_compositions(self, composition): def _deactivate_for_all_compositions(self): self._deactivate_for_all_compositions(ConnectionInfo.ALL) + def is_active_in_composition(self, composition): + return self.receiver.afferents_info[self].is_active_in_composition(composition) + def _delete_projection(projection, context=None): """Delete Projection, its entries in receiver and sender Ports, and in ProjectionRegistry""" projection.sender._remove_projection_from_port(projection) From 933daee694a5e130ce09e5ce4303757382639a4a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 25 Mar 2022 19:14:11 -0400 Subject: [PATCH 273/285] Composition: remove_projection: deactivate projection for self --- psyneulink/core/compositions/composition.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 01c8b69ff29..699a4a285f8 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -5799,7 +5799,11 @@ def remove_projection(self, projection): if projection in self.projections: self.projections.remove(projection) - # step 3 - TBI? remove Projection from afferents & efferents lists of any node + # step 3 - deactivate Projection in this Composition + projection._deactivate_for_compositions(self) + + # step 4 - TBI? remove Projection from afferents & efferents lists of any node + def _validate_projection(self, projection, From 448c35b67157b9418e5be5af0d8ec7a80e098f7c Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 25 Mar 2022 22:06:11 -0400 Subject: [PATCH 274/285] Scheduler: store original (not autogenerated) conditions --- psyneulink/core/scheduling/scheduler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/psyneulink/core/scheduling/scheduler.py b/psyneulink/core/scheduling/scheduler.py index ce265986788..f163dc19900 100644 --- a/psyneulink/core/scheduling/scheduler.py +++ b/psyneulink/core/scheduling/scheduler.py @@ -7,6 +7,7 @@ # ********************************************* Scheduler ************************************************************** +import copy import typing import graph_scheduler @@ -48,6 +49,9 @@ def __init__( if default_execution_id is None: default_execution_id = composition.default_execution_id + # TODO: consider integrating something like this into graph-scheduler? + self._user_specified_conds = copy.copy(conditions) + super().__init__( graph=graph, conditions=conditions, From 2c09f159c2d037dfc14714dfbf88b7e9607bdb1a Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Fri, 25 Mar 2022 22:07:13 -0400 Subject: [PATCH 275/285] Composition: reconstruct scheduler with original conditions --- psyneulink/core/compositions/composition.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 699a4a285f8..68183c123b8 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -3880,11 +3880,12 @@ def scheduler(self): """ if self.needs_update_scheduler or not isinstance(self._scheduler, Scheduler): old_scheduler = self._scheduler - self._scheduler = Scheduler(composition=self) - if old_scheduler is not None: - self._scheduler.add_condition_set(old_scheduler.conditions) + orig_conds = old_scheduler._user_specified_conds + else: + orig_conds = None + self._scheduler = Scheduler(composition=self, conditions=orig_conds) self.needs_update_scheduler = False return self._scheduler From 06d4b0852b4b4d2ff40857706ccf871b8bd17232 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Wed, 23 Mar 2022 01:12:37 -0400 Subject: [PATCH 276/285] Composition: fix node removal --- psyneulink/core/compositions/composition.py | 80 ++++++++---- tests/composition/test_composition.py | 127 ++++++++++++++++++++ 2 files changed, 182 insertions(+), 25 deletions(-) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 68183c123b8..1e1bd741109 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -4112,37 +4112,67 @@ def add_nodes(self, nodes, required_roles=None, context=None): f"({node}) must be a {Mechanism.__name__}, {Composition.__name__}, " f"or a tuple containing one of those and a {NodeRole.__name__} or list of them") + def remove_node(self, node): + self._remove_node(node) + + def _remove_node(self, node, analyze_graph=True): + for proj in node.afferents + node.efferents: + self.remove_projection(proj) + + for param_port in node.parameter_ports: + for proj in param_port.mod_afferents: + self.remove_projection(proj) + + # deactivate any shadowed projections + for shadow_target, shadow_port_original in self.shadowing_dict.items(): + if shadow_port_original in node.input_ports: + for shadow_proj in shadow_target.all_afferents: + if shadow_proj.sender.owner.composition is self: + self.remove_projection(shadow_proj) + + # NOTE: deactivation should be sufficient but + # asserts in OCM _update_state_input_port_names + # need target input ports of shadowed + # projections to be active or not present at all + try: + self.controller.state_input_ports.remove(shadow_target) + except AttributeError: + pass + + self.graph.remove_component(node) + del self.nodes_to_roles[node] + + # Remove any entries for node in required_node_roles or excluded_node_roles + node_role_pairs = [item for item in self.required_node_roles if item[0] is node] + for item in node_role_pairs: + self.required_node_roles.remove(item) + node_role_pairs = [item for item in self.excluded_node_roles if item[0] is node] + for item in node_role_pairs: + self.excluded_node_roles.remove(item) + + del self.nodes[node] + self.node_ordering.remove(node) + + for p in self.pathways: + try: + p.pathway.remove(node) + except ValueError: + pass + + self.needs_update_graph_processing = True + self.needs_update_scheduler = True + + if analyze_graph: + self._analyze_graph() + def remove_nodes(self, nodes): if not isinstance(nodes, (list, Mechanism, Composition)): assert False, 'Argument of remove_nodes must be a Mechanism, Composition or list containing either or both' nodes = convert_to_list(nodes) for node in nodes: - for proj in node.afferents + node.efferents: - try: - del self.projections[proj] - except ValueError: - # why are these not present? - pass + self._remove_node(node, analyze_graph=False) - try: - self.graph.remove_component(proj) - except CompositionError: - # why are these not present? - pass - - self.graph.remove_component(node) - del self.nodes_to_roles[node] - - # Remove any entries for node in required_node_roles or excluded_node_roles - node_role_pairs = [item for item in self.required_node_roles if item[0] is node] - for item in node_role_pairs: - self.required_node_roles.remove(item) - node_role_pairs = [item for item in self.excluded_node_roles if item[0] is node] - for item in node_role_pairs: - self.excluded_node_roles.remove(item) - - del self.nodes[node] - self.node_ordering.remove(node) + self._analyze_graph() @handle_external_context() def _add_required_node_role(self, node, role, context=None): diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index d201079f20f..889379c521c 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -1,3 +1,4 @@ +import collections import functools import logging from timeit import timeit @@ -18,6 +19,7 @@ from psyneulink.core.components.mechanisms.modulatory.control.optimizationcontrolmechanism import \ OptimizationControlMechanism from psyneulink.core.components.mechanisms.modulatory.learning.learningmechanism import LearningMechanism +from psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism import CompositionInterfaceMechanism from psyneulink.core.components.mechanisms.processing.integratormechanism import IntegratorMechanism from psyneulink.core.components.mechanisms.processing.objectivemechanism import ObjectiveMechanism from psyneulink.core.components.mechanisms.processing.processingmechanism import ProcessingMechanism @@ -7167,6 +7169,131 @@ def test_danglingControlledMech(self): comp.add_node(Reward) # no assert, should only complete without error + @pytest.mark.parametrize( + 'removed_nodes, expected_dependencies', + [ + (['A'], {'B': set(), 'C': set('B'), 'D': set('C'), 'E': set('C')}), + (['C'], {'A': set(), 'B': set(), 'D': set(), 'E': set()}), + (['E'], {'A': set(), 'B': set(), 'C': {'A', 'B'}, 'D': set('C')}), + (['A', 'B'], {'C': set(), 'D': set('C'), 'E': set('C')}), + (['D', 'E'], {'A': set(), 'B': set(), 'C': {'A', 'B'}}), + (['A', 'B', 'C', 'D', 'E'], {}), + ] + ) + def test_remove_node(self, removed_nodes, expected_dependencies): + def stringify_dependency_dict(dd): + return {node.name: {n.name for n in deps} for node, deps in dd.items()} + + A = pnl.TransferMechanism(name='A') + B = pnl.TransferMechanism(name='B') + C = pnl.TransferMechanism(name='C') + D = pnl.TransferMechanism(name='D') + E = pnl.TransferMechanism(name='E') + + locs = locals() + removed_nodes = [locs[n] for n in removed_nodes] + + comp = pnl.Composition( + pathways=[ + [A, C, D], + [A, C, D], + [B, C, D], + [B, C, E], + ] + ) + + comp.remove_nodes(removed_nodes) + + assert stringify_dependency_dict(comp.scheduler.dependency_dict) == expected_dependencies + assert stringify_dependency_dict(comp.graph_processing.dependency_dict) == expected_dependencies + + proj_dependencies = collections.defaultdict(set) + for node in comp.nodes: + if node not in removed_nodes: + for proj in node.afferents: + if ( + proj.sender.owner not in removed_nodes + and proj.receiver.owner not in removed_nodes + and not isinstance(proj.sender.owner, CompositionInterfaceMechanism) + and not isinstance(proj.receiver.owner, CompositionInterfaceMechanism) + ): + proj_dependencies[node.name].add(proj.name) + proj_dependencies[proj.name].add(proj.sender.owner.name) + assert stringify_dependency_dict(comp.graph.dependency_dict) == {**expected_dependencies, **proj_dependencies} + + for node in removed_nodes: + assert node not in comp.nodes + assert node not in comp.nodes_to_roles + assert node not in comp.graph.comp_to_vertex + assert node not in comp.graph_processing.comp_to_vertex + assert node not in comp.scheduler.conditions + + for proj in node.afferents + node.efferents: + assert proj not in comp.projections + assert not proj.is_active_in_composition(comp) + + comp.run(inputs={n: [0] for n in comp.get_nodes_by_role(pnl.NodeRole.INPUT)}) + + @pytest.mark.parametrize('slope_A', [1, (1, pnl.CONTROL)]) + @pytest.mark.parametrize('slope_B', [1, (1, pnl.CONTROL)]) + @pytest.mark.parametrize('removed_nodes', [['A'], ['B'], ['A', 'B']]) + def test_remove_node_control(self, slope_A, slope_B, removed_nodes): + A = pnl.TransferMechanism(name='A', function=pnl.Linear(slope=slope_A)) + B = pnl.TransferMechanism(name='B', function=pnl.Linear(slope=slope_B)) + + locs = locals() + removed_nodes = [locs[n] for n in removed_nodes] + + search_space_len = sum(1 if isinstance(s, tuple) else 0 for s in [slope_A, slope_B]) + + comp = pnl.Composition(pathways=[A, B]) + comp.add_controller( + pnl.OptimizationControlMechanism( + agent_rep=comp, search_space=[0, 1] * search_space_len + ) + ) + comp.remove_nodes(removed_nodes) + + for n in removed_nodes: + for proj in n.parameter_ports['slope'].all_afferents: + assert not proj.is_active_in_composition(comp), f'{n.name} {proj.name}' + + def test_remove_node_from_conditions(self): + def assert_conditions_do_not_contain(*args): + conds_queue = list(comp.scheduler.conditions.conditions.values()) + while len(conds_queue) > 0: + cur_cond = conds_queue.pop() + deps = [] + + try: + deps = [cur_cond.dependency] + except AttributeError: + try: + deps = cur_cond.dependencies + except AttributeError: + pass + + for d in deps: + if isinstance(d, pnl.Condition): + conds_queue.append(d) + assert d not in args + + A = pnl.TransferMechanism(name='A') + B = pnl.TransferMechanism(name='B') + C = pnl.TransferMechanism(name='C') + D = pnl.TransferMechanism(name='D') + + comp = pnl.Composition(pathways=[[A, D], [B, D], [C, D]]) + + comp.run(inputs={A: [0], B: [0], C: [0]}) + comp.remove_node(A) + comp.run(inputs={B: [0], C: [0]}) + assert_conditions_do_not_contain(A) + + comp.remove_node(B) + comp.run(inputs={C: [0]}) + assert_conditions_do_not_contain(A, B) + class TestInputSpecsDocumentationExamples: From 7dd6a50d0d29a1fc5bdeb6b5a9b9e798c906e6d8 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Sat, 26 Mar 2022 00:23:00 -0400 Subject: [PATCH 277/285] Composition: remove_projection: disable learning pathway if applicable if a projection being learned is removed, disable the entire pathway because the removal will break it --- psyneulink/core/compositions/composition.py | 23 ++++++++++++++++++++- tests/composition/test_composition.py | 16 ++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 1e1bd741109..975deb853a8 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -5833,7 +5833,28 @@ def remove_projection(self, projection): # step 3 - deactivate Projection in this Composition projection._deactivate_for_compositions(self) - # step 4 - TBI? remove Projection from afferents & efferents lists of any node + # step 4 - deactivate any learning to this Projection + for param_port in projection.parameter_ports: + for proj in param_port.mod_afferents: + self.remove_projection(proj) + if isinstance(proj.sender.owner, LearningMechanism): + for path in self.pathways: + # TODO: make learning_components values consistent type + try: + learning_mechs = path.learning_components['LEARNING_MECHANISMS'] + except KeyError: + continue + + if isinstance(learning_mechs, LearningMechanism): + learning_mechs = [learning_mechs] + + if proj.sender.owner in learning_mechs: + for mech in learning_mechs: + self.remove_node(mech) + self.remove_node(path.learning_components['objective_mechanism']) + self.remove_node(path.learning_components['TARGET_MECHANISM']) + + # step 5 - TBI? remove Projection from afferents & efferents lists of any node def _validate_projection(self, diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 889379c521c..959518a67f6 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -7294,6 +7294,22 @@ def assert_conditions_do_not_contain(*args): comp.run(inputs={C: [0]}) assert_conditions_do_not_contain(A, B) + def test_remove_node_learning(self): + A = ProcessingMechanism(name='A') + B = ProcessingMechanism(name='B') + C = ProcessingMechanism(name='C') + D = ProcessingMechanism(name='D') + + comp = Composition() + comp.add_linear_learning_pathway(pathway=[A, B], learning_function=BackPropagation) + comp.add_linear_learning_pathway(pathway=[C, D], learning_function=Reinforcement) + + comp.remove_node(A) + comp.learn(inputs={n: [0] for n in comp.get_nodes_by_role(pnl.NodeRole.INPUT)}) + + comp.remove_node(D) + comp.learn(inputs={n: [0] for n in comp.get_nodes_by_role(pnl.NodeRole.INPUT)}) + class TestInputSpecsDocumentationExamples: From 739a8aa447e39431b270cfe458b0c9a2d7950171 Mon Sep 17 00:00:00 2001 From: Katherine Mantel Date: Tue, 29 Mar 2022 20:00:09 -0400 Subject: [PATCH 278/285] requirements: restrict modelspec to working (#2366) --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index d0b01778816..02d2dd607e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ grpcio-tools<1.43.0 llvmlite<0.39 matplotlib<3.5.2 modeci_mdf>=0.3.2, <0.3.4 +modelspec<0.2.0 networkx<2.8 numpy<1.21.4, >=1.17.0 pillow<9.1.0 From 7afa4378926c12ab0b5b0186ea25f994685bcb59 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Tue, 29 Mar 2022 23:14:26 -0400 Subject: [PATCH 279/285] Test/compositon/ validate input dict keys (#2365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * • optimizationcontrolmechanism.py: docstring mods * • composition.py: - add allow_probes and exclude_probes_from_output * • composition.py: - docstring mods re: allow_probes • optimizationcontrolmechanism.py: - allow_probes: eliminate DIRECT setting - remove _parse_monitor_for_control_input_ports (no longer needed without allow_probes=DIRECT) * • composition.py: - change "exclude_probes_from_output" -> "include_probes_in_output" * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • composition.py: - docstring mods re: allow_probes and include_probes_in_output * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • controlmechanism.py: - add allow_probes handling (moved from OCM) • optimizationcontrolmechanism.py: - move allow_probes to controlmechanism.py • composition.py: - refactor handling of allow_probes to permit for any ControlMechanism - add _handle_allow_probes_for_control() to reconcile setting on Composition and ControlMechanism • objectivemechanism.py: - add modulatory_mechanism attribute * • composition.py add assignment of learning_mechanism to objective_mechanism.modulatory_mechanism for add_learning methods * • docstring mods * - * - * • optimizationcontrolmechanism.py: docstring revs * - * - * • test_composition.py: - add test_unnested_PROBE - add test_nested_PROBES TBD: test include_probes_in_output * - * • composition.py - add_node(): support tuple with required_role * - * • composition.py: - _determine_node_roles: fix bug in which nested comp was prevented from being an OUTPUT Node if, in addition to Nodes that qualifed as OUTPUT, it also had nodes that projected to Nodes in an outer comp (making it look like it was INTERNAL) * - * • composition.py: - add_node(): enforce include_probes_in_output = True for nested Compositions - execute(): - replace return of output_value with get_output_value() * - * • CompositionInterfaceMechanism.rst: - correct path ref • compositioninterfacemechanism.py: - docstring fixes * • optimizationcontrolmechanism.py: - docstring edits * - * - * - * - * - * - * - * - * • composition.py, optimizationcontrolmechanism.py: _build_predicted_input_dict(): support partial specification of INPUT Nodes for nested Compositions of agent_rep * • test_control.py: - test_nested_composition_as_agent_rep(): PASSES ALL TESTS * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: support nested composition in dict spec * • test_control.py - test_ocm_state_feature_specs_and_warnings_and_errors(): - modified to accomodate nested INPUT Node specs - PASSES ALL TESTS * • composition.py: - add _nodes_added attribute - add_nodes(): set _nodes_added attribute upon completition - _analyze_graph(): add call _determine_node_roles() if _nodes_added is True * • composition.py: - _nodes_added -> needs_determine_node_roles • optimizationcontrolmechanism.py: - _get_agent_rep_input_nodes(): call _determine_node_roles if agent_rep.needs_determine_node_roles is True * • optimizationcontrolmechanism.py: - _parse_state_feature_specs(): consolidate handling of various formats * • optimizationcontrolmechanism.py: docstring updates * • optimizationcontrolmechanism.py: _get_agent_rep_input_nodes() -> _get_agent_rep_input_receivers() _parse_state_feature_specs(): standardize on InputPorts rather than Nodes * - * - * - * - * • composition.py: - _instantiate_input_dict(): refactor to accept inputs for InputPorts of nested Nodes - add helper method _get_external_cim_input_port() - TBD: - refactor _build_predicted_inputs_dict to support above * - * • composition.py: - _instantiate_input_dict(): now generates properly formatted values in input_dict for InputPort specs * • composition.py: - _build_predicted_inputs_dict(): refactored to construct inputs_dict with InputPorts as keys * - * - * - * - * - * - * - * - * - * - * - * - * - * • optimizationcontrolmechanism.py: - refactor state_feature_values as dict that can be used as predicted_inputs / feature_values arg of evaluate() (and inputs arg of run) • composition.py: - remove _build_predicted_inputs_dict(), since state_feature_values now formatted properly as input * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): BEFORE REFACTORING FOR MISSING PORTS OF NESTED COMP * • composition.py: - _instantiate_inputs_dict(): AFTER REFACTORING FOR MISSING PORTS OF NESTED COMP * - * - * - * - * • test_composition.py: - refactor test_input_type_equivalence() * - PASSES test_control * - merged from devel missing dependencies * - * - * - * • update pandas req to 1.4.1 * • composition.py: - _instantiate_inputs_dict(): refactored to consolidate treatment of nested Nodes * - * - * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * • composition.py - _instantiate_input_dict(): handle ports with diff numbers of trials specified * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * • composition.py: - _parse_names_in_inputs(): support Port.full_name as keys in inputs dict • mechanism.py: - Mechanism_Base: replaced input_labels and output_labels with labeled_input_values and labeled_output_values respectively • port.py, inputport.py, outputport.py, parameterport.py: - Port_Base: impelment labeled_value property (and value_label alias) that returns calls get_label() - deleted label attributes of InputPort and OutputPort (replaced by Port_Base.labeled_value) - added get_label() stub on ParameterPort with error message * - * - * • conf.py: comment out 'sphinx_autodoc_typehints' (crashing for sphinx_autodoc_typehints v.1.17.0) * • conf.py: restore 'sphinx_autodoc_typehints' • doc_requirements.txt: pin sphinx_autodoc_typehints < v.1.16.0; error on v1.16 and v1.17; error: https://github.com/PrincetonUniversity/PsyNeuLink/runs/5573651890?check_suite_focus=true * - * - * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * • composition.py: - add property: external_input_ports_of_all_input_nodes • optimizationcontrolmechanism.py: - implement state_faature_default - add SHADOW_INPUTS as individual spec for state_features * - * - * - * - * - * - * - * - * • inputport.py: - add figure for shadowing * - * - * • processingmechanism.py: - __init__(): modify typecheck for input_ports to allow single items * • optimizationcontrolmechanism.py - allow single spec (None, array, tuple, or Components) that is assigned to all INPUT Node InputPorts - state_feature_default is assigned to all unspecified INPUT Node InputPorts (for a list that is shorter than the number, a dict or a set that has fewer, or any that are added to agent_rep after controller is initially constructed and added to Composition) * - * - * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * • optimizationcontrolmechanism.py: docstring mods * - * - * - * - * - * • optimizationcontrolmechanism.py: - _state_feature_values_getter(): for numeric state_feature, return state_input_port.functio(numeric_value) * • test_control.py: - test_ocm_state_feature_specs_and_warnings_and_errors() - added tests for single state_feature spec * - composition.py, inputport.py, optimizationcontrolmechanism.py: docstring mods re: "external InputPort" * - * - * - * • test_control.py: - test_state_features_in_nested_composition_as_agent_rep(): - add tests for single state_feature specs - add tests for INPUT Node with more than on InputPort * - * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * - * - * • optimizationcontrolmechanism.py: - state_features, get_state_feature_source: simplify * • optimizationcontrolmechanism.py: IN PROGRESS - _specified_INPUT_Node_InputPorts_in_order: use name (str) of items not yet in agent_rep rather than 'None' - state_features & state_feature_values: - use name (str) as key if INPUT Node InputPort is not yet in agent_rep - use name (str) if source (spec) is not yet in Composition * - * • optimizationcontrolmechanism.py: IMPLEMENTED - state_features: - convert all keys and values to port.full_name - if not yet in comp or agent_rep, add "DEFERRED..." * • optimizationcontrolmechanism.py: PASSES deferred_init and partial_deferred_init tests * • optimizationcontrolmechanism.py: - state_feature_values: strings for missing keys or values * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_deferred and test_partial_deferred * • test_control.py: PASSING test_ocm_state_feature_specs_and_warnings_and_errors * • test_control.py: PASSING all state_feature tests * • composition.py: add _is_in_composition method * - * - * - * - * - * - * - * - * - * • optimzationcontrolmechanism.py: fix figure * • optimzationcontrolmechanism.py: fix figure * • optimizationcontrolmechanism.py: - fix bug in which state_feature_default = None was being ignored - fix bug in which deferred nodes were not being assigned state_feature_default * - * • optimizationcontrolmechanism.py: - fix bug in which state_feature_default = None was being ignored - fix bug in which deferred nodes were not being assigned state_feature_default * • test_control.py: - test_deferred_init, test_partial_deferred_init(): added tests for controller.state_input_ports path_afferents * - * - * • optimizationcontrolmechanism.py: - _parse_state_feature_specs: - fixed bug in handling of lists of numeric specs - renamed state_input_ports for numeric specs * • registry.py: - rename_instance_in_registry(): finish implementing (including **new_name** arg) • optimizationcontrolmechanism.py: - _update_state_features_dict: rename deferred state_input_ports * • registry.py: - rename_instance_in_registry(): finish implementing (including **new_name** arg) • optimizationcontrolmechanism.py: - _update_state_features_dict: rename deferred state_input_ports * - * • optimizationcontrolmechanism.py, test_control.py - standarize prefixes for state_input_ports * • optimizationcontrolmechanism.py, test_control.py - standarize prefixes for state_input_ports * - * - * • test_show_graph remaining * • mechanism.py: - _show_structure(): fix parameterport label bug * - * - * - * - * • composition.py: _validate_input_keys(): add method to validate keys of inputs to run() method * • test_composition.py: - test_inputs_key_errors() * • test_composition.py: - test_inputs_key_errors(): add test for running nested composition with inputs * - * • composition.py: - execute(): allow direct call to nested composition with inputs * - * - Co-authored-by: jdcpni --- .../core/components/mechanisms/mechanism.py | 7 +- .../control/optimizationcontrolmechanism.py | 5 -- psyneulink/core/compositions/composition.py | 48 ++++++++++-- tests/composition/test_composition.py | 78 ++++++++++++++++++- 4 files changed, 121 insertions(+), 17 deletions(-) diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 1d3e7cba786..e174f9004e2 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2406,11 +2406,10 @@ def execute(self, """ if self.initialization_status == ContextFlags.INITIALIZED: - context.string = "{} EXECUTING {}: {}".format(context.source.name,self.name, - ContextFlags._get_context_string( - context.flags, EXECUTION_PHASE)) + context.string = f"{context.source.name} EXECUTING {self.name}: " \ + f"{ContextFlags._get_context_string(context.flags, EXECUTION_PHASE)}." else: - context.string = "{} INITIALIZING {}".format(context.source.name, self.name) + context.string = f"{context.source.name} INITIALIZING {self.name}." if context.source is ContextFlags.COMMAND_LINE: self._initialize_from_context(context, override=False) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 35ed4c778ba..f5f4f2b0a0b 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1172,11 +1172,6 @@ def _state_feature_values_getter(owning_component=None, context=None): len(specified_INPUT_Node_InputPorts) == \ owning_component.num_state_input_ports - # If OptimizationControlMechanism is still under construction, use items from input_values as placemarkers - # if context.source == ContextFlags.CONSTRUCTOR: - # return {k:v for k,v in zip(specified_INPUT_Node_InputPorts, - # owning_component.input_values[owning_component.num_outcome_input_ports:])} - # Construct state_feature_values dict state_feature_values = {} for i in range(owning_component.num_state_input_ports): diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 975deb853a8..629225b6e0d 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -4071,7 +4071,8 @@ def add_node(self, node, required_roles=None, context=None): self._need_check_for_unused_projections = True - # # MODIFIED 1/27/22 NEW: FIX - BREAKS test_learning_output_shape() in ExecuteMode.LLVM + # # MODIFIED 1/27/22 NEW - FIX - BREAKS test_learning_output_shape() in ExecuteMode.LLVM + # [3/25/22] STILL NEEDED (e.g., FOR test_inputs_key_errors() # if context.source != ContextFlags.METHOD: # # Call _analyze_graph with ContextFlags.METHOD to avoid recursion # self._analyze_graph(context=Context(source=ContextFlags.METHOD)) @@ -8454,6 +8455,9 @@ def evaluate( buffer_animate_state = self._animate # Run Composition in "SIMULATION" context + # # MODIFIED 3/28/22 NEW: + # context.source = ContextFlags.COMPOSITION + # MODIFIED 3/28/22 END context.add_flag(ContextFlags.SIMULATION_MODE) context.remove_flag(ContextFlags.CONTROL) @@ -9269,7 +9273,11 @@ def _validate_execution_inputs(self, inputs): # EXECUTION # ****************************************************************************************************************** + # MODIFIED 3/28/22 OLD: @handle_external_context() + # # MODIFIED 3/28/22 NEW: + # @handle_external_context(source = ContextFlags.COMMAND_LINE) + # MODIFIED 3/28/22 END def run( self, inputs=None, @@ -9527,7 +9535,9 @@ def run( trials. """ + # MODIFIED 3/28/22 OLD: context.source = ContextFlags.COMPOSITION + # MODIFIED 3/28/22 END execution_phase = context.execution_phase context.execution_phase = ContextFlags.PREPARING @@ -9974,6 +9984,9 @@ def learn( runner = CompositionRunner(self) context.add_flag(ContextFlags.LEARNING_MODE) + # # MODIFIED 3/28/22 NEW: + # context.source = ContextFlags.COMPOSITION + # MODIFIED 3/28/22 END # # FIX 5/28/20 # context.add_flag(ContextFlags.PREPARING) # context.execution_phase=ContextFlags.PREPARING @@ -10220,11 +10233,15 @@ def execute( # These are meant to be assigned in run method; needed here for direct call to execute method self._animate = False - # KAM Note 4/29/19 + # IMPLEMENTATION NOTE: + # KAM 4/29/19 # The nested var is set to True if the Composition is nested in another Composition, otherwise False # Later on, this is used to determine: # (1) whether to initialize from context # (2) whether to assign values to CIM from input dict (if not nested) or simply execute CIM (if nested) + # JDC 3/28/22: + # This currently prevents a Composition that is nested within another to be tested on its own + # Would be good to figure out a way to accomodate that nested = False if len(self.input_CIM.path_afferents) > 0: nested = True @@ -10390,15 +10407,32 @@ def execute( # but node execution of nested compositions with # outside control is not supported yet. assert not nested or len(self.parameter_CIM.afferents) == 0 + elif nested: - # check that inputs are specified - autodiff does not in some cases - if ContextFlags.SIMULATION_MODE in context.runmode and inputs is not None: - self.input_CIM.execute(build_CIM_input, context=context) + + # MODIFIED 3/28/22 CURRENT: + # IMPLEMENTATION NOTE: context.string set in Mechanism.execute + direct_call = (f"{context.source.name} EXECUTING" not in context.string) + # MODIFIED 3/28/22 NEW: + # direct_call = (context.source == ContextFlags.COMMAND_LINE) + # MODIFIED 3/28/22 END + simulation = ContextFlags.SIMULATION_MODE in context.runmode + if simulation or direct_call: + # For simulations, or direct call to nested Composition (e.g., from COMMAND_LINE to test it) + # assign inputs if they not provided (e.g., # autodiff) + if inputs is not None: + self.input_CIM.execute(build_CIM_input, context=context) + else: + self.input_CIM.execute(context=context) else: - assert inputs is None, f"Input provided to nested Composition {self.name}; run() method should " \ - f"only be called on outer-most composition within which it is nested." + # regular run (DEFAULT_MODE) of nested Composition called from enclosing Composition, + # so inputs should be None, and be assigned from nested Composition's input_CIM + assert inputs is None,\ + f"Input provided to a nested Composition {self.name} in call from outer composition." self.input_CIM.execute(context=context) + self.parameter_CIM.execute(context=context) + else: self.input_CIM.execute(build_CIM_input, context=context) diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 959518a67f6..1f06cce8a50 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -28,7 +28,7 @@ from psyneulink.core.components.ports.modulatorysignals.controlsignal import ControlSignal, CostFunctions from psyneulink.core.components.projections.modulatory.controlprojection import ControlProjection from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection -from psyneulink.core.compositions.composition import Composition, CompositionError, NodeRole +from psyneulink.core.compositions.composition import Composition, NodeRole, CompositionError, RunError from psyneulink.core.compositions.pathway import Pathway, PathwayRole from psyneulink.core.globals.context import Context from psyneulink.core.globals.keywords import \ @@ -2606,6 +2606,82 @@ def test_input_not_provided_to_run(self): assert np.allclose(T.parameters.value.get(C), [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]) assert np.allclose(run_result, [[np.array([2.0, 4.0])]]) + input_args = [ + ('non_input_node', + '"The following items specified in the \'inputs\' arg of the run() method for \'Composition-1\' ' + 'are not INPUT Nodes of that Composition (nor InputPorts of them): \'OB\'."' + ), + ('non_input_port', + '"The following items specified in the \'inputs\' arg of the run() method for \'Composition-1\' ' + 'that are not a Mechanism, Composition, or an InputPort of one: \'OA[OutputPort-0]\'."' + ), + ('nested_non_input_node', + '"The following items specified in the \'inputs\' arg of the run() method for \'Composition-1\' ' + 'are not INPUT Nodes of that Composition (nor InputPorts of them): \'IB\'."' + ), + ('nested_non_input_port', + '"The following items specified in the \'inputs\' arg of the run() method for \'Composition-1\' ' + 'that are not a Mechanism, Composition, or an InputPort of one: \'IA[OutputPort-0]\'."' + ), + ('input_port_and_mech', + '"The \'inputs\' arg of the run() method for \'Composition-1\' includes specifications of ' + 'the following InputPorts *and* the Mechanisms to which they belong; ' + 'only one or the other can be specified as inputs to run(): OA[InputPort-0]."' + ), + ('nested_input_port_and_comp', + '"The \'inputs\' arg of the run() method for \'Composition-1\' includes specifications of ' + 'the following InputPorts or Mechanisms *and* the Composition within which they are nested: ' + '[(\'IA[InputPort-0]\', \'Composition-0\')]."' + ), + ('nested_mech_and_comp', + '"The \'inputs\' arg of the run() method for \'Composition-1\' includes specifications of ' + 'the following InputPorts or Mechanisms *and* the Composition within which they are nested: ' + '[(\'IA\', \'Composition-0\')]."' + ), + ('run_nested_with_inputs', None) + ] + @pytest.mark.parametrize('input_args', input_args, ids=[x[0] for x in input_args]) + def test_inputs_key_errors(self, input_args): + + condition = input_args[0] + expected_error_text = input_args[1] + + ia = ProcessingMechanism(name='IA') + ib = ProcessingMechanism(name='IB') + oa = ProcessingMechanism(name='OA') + ob = ProcessingMechanism(name='OB') + icomp = Composition([ia, ib]) + ocomp = Composition([oa, ob]) + ocomp.add_node(icomp) + + if condition in {'nested_non_input_node', 'nested_non_input_port'}: + X = ia + Y = ib + else: + X = oa + Y = ob + + if 'non_input_node' in condition: + inputs={X:[1], Y:[1]} + elif 'non_input_port' in condition: + inputs={X.output_port:[1]} + elif condition == 'input_port_and_mech': + inputs={X.input_port:[1], X:[1]} + elif condition == 'nested_input_port_and_comp': + inputs={ia.input_port:[1], icomp:[1]} + elif condition == 'nested_mech_and_comp': + inputs={ia:[1], icomp:[1]} + elif condition == 'run_nested': + inputs={ia:[1]} + + if expected_error_text: + with pytest.raises(RunError) as error_text: + ocomp.run(inputs=inputs) + assert expected_error_text in str(error_text.value) + else: + ocomp._analyze_graph() + icomp.run(inputs={ia:[1]}) + def test_some_inputs_not_specified(self): comp = Composition() From 9fd94709f49e9073139f14a8a5272e6eca73ef49 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 30 Mar 2022 00:15:03 -0400 Subject: [PATCH 280/285] llvm, functions/DDI: Add human readable name to alloca instruction Signed-off-by: Jan Vesely --- .../core/components/functions/stateful/integratorfunctions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/functions/stateful/integratorfunctions.py b/psyneulink/core/components/functions/stateful/integratorfunctions.py index 65dd4b596b8..dd344d3b9f0 100644 --- a/psyneulink/core/components/functions/stateful/integratorfunctions.py +++ b/psyneulink/core/components/functions/stateful/integratorfunctions.py @@ -2526,7 +2526,7 @@ def _gen_llvm_integrate(self, builder, index, ctx, vi, vo, params, state): time_step_size = self._gen_llvm_load_param(ctx, builder, params, index, TIME_STEP_SIZE) random_state = ctx.get_random_state_ptr(builder, self, state, params) - rand_val_ptr = builder.alloca(ctx.float_ty) + rand_val_ptr = builder.alloca(ctx.float_ty, name="random_out") rand_f = ctx.get_normal_dist_function_by_state(random_state) builder.call(rand_f, [random_state, rand_val_ptr]) rand_val = builder.load(rand_val_ptr) From 1be564c5e0d1d319aca2ce2f39a085e891b9c22a Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 29 Mar 2022 21:45:51 -0400 Subject: [PATCH 281/285] llvm: Drop 'competition' parameter from compiled structure Not used in compiled computation. Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 780673f3953..7f2466b0257 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1399,7 +1399,7 @@ def _get_compilation_params(self): "allocation_samples", "control_allocation_search_space", # not used in computation "auto", "hetero", "cost", "costs", "combined_costs", - "control_signal", "intensity", + "control_signal", "intensity", "competition", "has_recurrent_input_port", "enable_learning", "enable_output_type_conversion", "changes_shape", "output_type", "bounds", "internal_only", From dad3c3a45482f3bbe680f3683d90b854cb9540b2 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Tue, 29 Mar 2022 21:52:58 -0400 Subject: [PATCH 282/285] llvm: Drop 'smoothing_factor' parameter from compiled structure Not used in compiled computation. Signed-off-by: Jan Vesely --- psyneulink/core/components/component.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 7f2466b0257..bbe4c760afe 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1409,7 +1409,8 @@ def _get_compilation_params(self): "adjustment_cost", "intensity_cost", "duration_cost", "enabled_cost_functions", "control_signal_costs", "default_allocation", "same_seed_for_all_allocations", - "search_statefulness", "initial_seed", "combine" + "search_statefulness", "initial_seed", "combine", + "smoothing_factor", } # Mechanism's need few extra entires: # * matrix -- is never used directly, and is flatened below From 26736b84b6727de57d3c3994e3f92ef372e283d4 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 30 Mar 2022 10:06:56 -0400 Subject: [PATCH 283/285] llvm/builtins: Convert array indexing to conditional select The mag01 array has two constant elements. These don't have to be loaded via stack location, however, since the array is dynamically indexed, the compiler won't move the constants to registers. Perform this transformation manually to reduce stack usage. Signed-off-by: Jan Vesely --- psyneulink/core/llvm/builtins.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/psyneulink/core/llvm/builtins.py b/psyneulink/core/llvm/builtins.py index 57bb6bb88de..a64be3d4c6a 100644 --- a/psyneulink/core/llvm/builtins.py +++ b/psyneulink/core/llvm/builtins.py @@ -640,8 +640,8 @@ def _setup_mt_rand_integer(ctx, state_ty): cond = builder.icmp_signed(">=", idx, ctx.int32_ty(_MERSENNE_N)) with builder.if_then(cond, likely=False): mag01 = ir.ArrayType(array.type.pointee.element, 2)([0, 0x9908b0df]) - pmag01 = builder.alloca(mag01.type, name="mag01") - builder.store(mag01, pmag01) + mag0 = builder.extract_value(mag01, [0]) + mag1 = builder.extract_value(mag01, [1]) with helpers.for_loop_zero_inc(builder, ctx.int32_ty(_MERSENNE_N - _MERSENNE_M), @@ -653,9 +653,9 @@ def _setup_mt_rand_integer(ctx, state_ty): val_kk_1 = b.and_(b.load(pkk_1), pkk_1.type.pointee(0x7fffffff)) val = b.or_(val_kk, val_kk_1) - val_1 = b.and_(val, val.type(1)) - pval_mag = b.gep(pmag01, [ctx.int32_ty(0), val_1]) - val_mag = b.load(pval_mag) + val_i1 = b.and_(val, val.type(1)) + val_b = b.trunc(val_i1, ctx.bool_ty) + val_mag = b.select(val_b, mag1, mag0) val_shift = b.lshr(val, val.type(1)) @@ -681,9 +681,9 @@ def _setup_mt_rand_integer(ctx, state_ty): val_kk_1 = b.and_(b.load(pkk_1), pkk.type.pointee(0x7fffffff)) val = b.or_(val_kk, val_kk_1) - val_1 = b.and_(val, val.type(1)) - pval_mag = b.gep(pmag01, [ctx.int32_ty(0), val_1]) - val_mag = b.load(pval_mag) + val_i1 = b.and_(val, val.type(1)) + val_b = b.trunc(val_i1, ctx.bool_ty) + val_mag = b.select(val_b, mag1, mag0) val_shift = b.lshr(val, val.type(1)) From a2a267bf06ddaf2b3bcef21d42b5482729740733 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 30 Mar 2022 10:19:30 -0400 Subject: [PATCH 284/285] llvm, mechanisms/OCM: Use more descriptive variables names num_runs -> num_sim_trials num_inputs -> num_sim_inputs Signed-off-by: Jan Vesely --- .../modulatory/control/optimizationcontrolmechanism.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index f5f4f2b0a0b..3b4808f2d81 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -3397,19 +3397,19 @@ def _gen_llvm_evaluate_function(self, *, ctx:pnlvm.LLVMBuilderContext, tags=froz param_is_zero = builder.icmp_unsigned("==", num_trials_per_estimate, ctx.int32_ty(0)) num_sims = builder.select(param_is_zero, ctx.int32_ty(1), - num_trials_per_estimate, "corrected_estimates") + num_trials_per_estimate, "corrected_trials per_estimate") - num_runs = builder.alloca(ctx.int32_ty, name="num_runs") - builder.store(num_sims, num_runs) + num_trials = builder.alloca(ctx.int32_ty, name="num_sim_trials") + builder.store(num_sims, num_trials) # We only provide one input - num_inputs = builder.alloca(ctx.int32_ty, name="num_inputs") + num_inputs = builder.alloca(ctx.int32_ty, name="num_sim_inputs") builder.store(num_inputs.type.pointee(1), num_inputs) # Simulations don't store output comp_output = sim_f.args[4].type(None) builder.call(sim_f, [comp_state, comp_params, comp_data, comp_input, - comp_output, num_runs, num_inputs]) + comp_output, num_trials, num_inputs]) # Extract objective mechanism value idx = self.agent_rep._get_node_index(self.objective_mechanism) From 98f353bf453eef06f53b599e4f7d0f785572fec2 Mon Sep 17 00:00:00 2001 From: jdcpni Date: Fri, 1 Apr 2022 12:42:41 -0400 Subject: [PATCH 285/285] Refactor/ocm/state features all as input ports (#2369) --- .../nonstateful/transferfunctions.py | 69 ++-- .../control/optimizationcontrolmechanism.py | 107 +++-- psyneulink/core/compositions/composition.py | 36 +- tests/composition/test_control.py | 379 ++++++++++++++---- tests/composition/test_show_graph.py | 8 +- 5 files changed, 440 insertions(+), 159 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/transferfunctions.py b/psyneulink/core/components/functions/nonstateful/transferfunctions.py index ebb2bbc9b74..5bce6445bba 100644 --- a/psyneulink/core/components/functions/nonstateful/transferfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/transferfunctions.py @@ -761,15 +761,19 @@ class Logistic(TransferFunction): # ------------------------------------------- `function ` returns logistic transform of `variable `: .. math:: - \\frac{1}{1 + e^{ - gain ( variable + bias - x_{0}) + offset}} + scale * \\frac{1}{1 + e^{ - gain ( variable + bias - x_{0} ) + offset}} - (this is an offset and scaled version of the `Tanh`, which is centered on origin). + (this is a vertically offset and scaled version of `Tanh`, which is centered on origin). + + .. _Logistic_Note: .. note:: - The **bias** and **x_0** arguments are identical, apart from opposite signs: **bias** is included to - accomodate the convention in the machine learning community; **x_0** is included to match the `standard + The **bias** and **x_0** arguments are identical, apart from having opposite signs: **bias** is included to + accommodate the convention in the machine learning community; **x_0** is included to match the `standard form of the Logistic Function `_ (in which **gain** - corresponds to the *k* parameter and **scale** corresponds to the *L* parameter). + corresponds to the *k* parameter and **scale** corresponds to the *L* parameter); **offset** implements a + form of bias that is not modulated by gain (i.e., it produces an offset of the function along the horizontal + axis). `derivative ` returns the derivative of the Logistic using its **output**: @@ -783,22 +787,26 @@ class Logistic(TransferFunction): # ------------------------------------------- specifies a template for the value to be transformed. gain : float : default 1.0 - specifies value by which to multiply `variable ` before logistic transformation + specifies value by which to multiply each element of `variable ` after it is adjusted by + `bias ` and/or `x_0 `, but before adjustment by `offset ` and + logistic transformation (see `note ` above). bias : float : default 0.0 - specifies value to add to each element of `variable ` before applying `gain ` - and before logistic transformation. This argument is identical to x_0, with the opposite sign. + specifies value to add to each element of `variable ` before applying `gain `; + this argument has an effect identical to x_0, but with the opposite sign (see `note ` above). x_0 : float : default 0.0 - specifies value to subtract from each element of `variable ` before applying `gain ` - and before logistic transformation. This argument is identical to bias, with the opposite sign. + specifies value to add to each element of `variable ` before applying `gain `; + this argument has an effect identical to bias, but with the opposite sign (see `note ` above). offset : float : default 0.0 - specifies value to add to each element of `variable ` after applying `gain ` - but before logistic transformation. + specifies value to add to each element of `variable ` after adjusting by `bias + ` and/or `x_0 ` and applying `gain `, but before logistic + transformation (see `note ` above). scale : float : default 0.0 - specifies value by which each element is multiplied after applying the logistic transformation. + specifies value by which to multiply each element of `variable ` after all other parameters + and logistic transformation have been applied (see `note ` above). params : Dict[param keyword: param value] : default None a `parameter dictionary ` that specifies the parameters for the @@ -820,26 +828,33 @@ class Logistic(TransferFunction): # ------------------------------------------- variable : number or array contains value to be transformed. - gain : float : default 1.0 - value by which each element of `variable ` is multiplied before applying the - `bias ` (if it is specified). + gain : float + value by which to multiply each element of `variable ` after it is adjusted by `bias + ` and/or `x_0 `, but before adjustment by `offset ` and + logistic transformation (see `note ` above). - bias : float : default 0.0 - value added to each element of `variable ` before applying the `gain ` - (if it is specified). This attribute is identical to x_0, with the opposite sign. + bias : float + value to add to each element of `variable ` before applying `gain `; + this argument has an effect identical to x_0, but with the opposite sign (see `note ` above). - x_0 : float : default 0.0 - value subtracted from each element of `variable ` before applying the `gain ` - (if it is specified). This attribute is identical to bias, with the opposite sign. + x_0 : float + value to add to each element of `variable ` before applying `gain `; + this argument has an effect identical to bias, but with the opposite sign (see `note ` above). - offset : float : default 0.0 - value to added to each element of `variable ` after applying `gain ` - but before logistic transformation. + offset : float + value to add to each element of `variable ` after adjusting by `bias ` + and/or `x_0 ` and applying `gain `, but before logistic transformation + (see `note ` above). - scale : float : default 0.0 - value by which each element is multiplied after applying the Logistic transform. + scale : float + value by which to multiply each element of `variable ` after all other parameters and + logistic transformation have been applied (see `note ` above). bounds : (0,1) + COMMENT: + the lower and upper limits of the result which, in the case of the `Logistic`, is determined by the function + itself. + COMMENT owner : Component `component ` to which the Function has been assigned. diff --git a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py index 3b4808f2d81..9b7517f0f60 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/optimizationcontrolmechanism.py @@ -1097,7 +1097,7 @@ from psyneulink.core.globals.keywords import \ ALL, COMPOSITION, COMPOSITION_FUNCTION_APPROXIMATOR, CONCATENATE, DEFAULT_INPUT, DEFAULT_VARIABLE, EID_FROZEN, \ FUNCTION, INPUT_PORT, INTERNAL_ONLY, NAME, OPTIMIZATION_CONTROL_MECHANISM, NODE, OWNER_VALUE, PARAMS, PORT, \ - PROJECTIONS, SHADOW_INPUTS, SHADOW_INPUT_NAME, VALUE + PROJECTIONS, SHADOW_INPUTS, VALUE from psyneulink.core.globals.registry import rename_instance_in_registry from psyneulink.core.globals.parameters import Parameter from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel @@ -2118,13 +2118,13 @@ def _parse_state_feature_specs(self, context=None): if (isinstance(self.state_feature_specs, set) or isinstance(self.state_feature_specs, dict) and SHADOW_INPUTS not in self.state_feature_specs): # Dict and set specs reference Nodes that are not yet in agent_rep - warnings.warn(f"Nodes been specified in the {STATE_FEATURES}' arg for '{self.name}' that are not " + warnings.warn(f"Nodes are specified in the {STATE_FEATURES}' arg for '{self.name}' that are not " f"(yet) in its its {AGENT_REP} ('{self.agent_rep.name}'). They must all be assigned " f"to it before the Composition is executed'. It is generally safer to assign all " f"Nodes to the {AGENT_REP} of a controller before specifying its '{STATE_FEATURES}'.") else: # List and SHADOW_INPUTS specs are dangerous before agent_rep has been fully constructed - warnings.warn(f"The {STATE_FEATURES}' arg for '{self.name}' has been specified before any Nodes have " + warnings.warn(f"The '{STATE_FEATURES}' arg for '{self.name}' has been specified before any Nodes have " f"been assigned to its {AGENT_REP} ('{self.agent_rep.name}'). Their order must be the " f"same as the order of the corresponding INPUT Nodes for '{self.agent_rep.name}' once " f"they are added, or unexpected results may occur. It is safer to assign all Nodes to " @@ -2590,10 +2590,12 @@ def _update_state_input_ports_for_controller(self, context=None): (note: validation of state_features specified for CompositionFunctionApproximator optimization is up to the CompositionFunctionApproximator) - If agent_rep is a Composition, call: - - _update_state_input_port_names() - - _update_state_features_dict() - - _validate_state_features() + If agent_rep is a Composition: + - if has any new INPUT Node InputPorts: + - construct state_input_ports for them + - add to _specified_INPUT_Node_InputPorts_in_order + - call _validate_state_features() + - call _update_state_input_port_names() """ # Don't instantiate unless being called by Composition.run() @@ -2611,17 +2613,11 @@ def _update_state_input_ports_for_controller(self, context=None): from psyneulink.core.compositions.composition import Composition num_agent_rep_input_ports = len(self.agent_rep_input_ports) num_state_feature_specs = len(self.state_feature_specs) - # if self.num_state_input_ports != num_agent_rep_input_ports: + if num_state_feature_specs < num_agent_rep_input_ports: # agent_rep is Composition, but state_input_ports are missing for some agent_rep INPUT Node InputPorts # so construct a state_input_port for each missing one, using state_feature_default; # note: assumes INPUT Nodes added are at the end of the list in self.agent_rep_input_ports - # FIX: 3/24/22 - HANDLED ELSEWHWERE WITH ERROR MESSAGE (SHOULD FIGURE OUT WHERE) - # if context.flags & ContextFlags.PREPARING: - # # At run time, insure that there not *more* state_input_ports than agent_rep INPUT Node InputPorts - # assert self.num_state_input_ports < len(self.agent_rep_input_ports), \ - # f"PROGRAM ERROR: More state_input_ports assigned to '{self.name}' ({self.num_state_input_ports}) " \ - # f"than agent_rep ('{self.agent_rep.name}') has INPUT Node InputPorts ({num_agent_rep_input_ports})." # FIX: 3/24/22 - REFACTOR THIS TO CALL _parse_state_feature_specs? state_input_ports = [] local_context = Context(source=ContextFlags.METHOD) @@ -2635,7 +2631,7 @@ def _update_state_input_ports_for_controller(self, context=None): continue if default == SHADOW_INPUTS: params[SHADOW_INPUTS] = input_port - input_port_name = f"{SHADOW_INPUT_NAME}{input_port.full_name}]" + input_port_name = _shadowed_state_input_port_name(input_port.full_name, input_port.full_name) self.state_feature_specs.append(input_port) elif is_numeric(default): params[VALUE]: default @@ -2664,39 +2660,84 @@ def _update_state_input_ports_for_controller(self, context=None): # Assign OptimizationControlMechanism attributes self.state_input_ports.extend(state_input_ports) - self._specified_INPUT_Node_InputPorts_in_order = self.agent_rep_input_ports - self._update_state_input_port_names() + # IMPLEMENTATION NOTE: Can't just assign agent_rep_input_ports to _specified_INPUT_Node_InputPorts_in_order + # below since there may be specifications in _specified_INPUT_Node_InputPorts_in_order + # for agent_rep INPUT Node InputPorts that have not yet been added to Composition + # (i.e., they are deferred) + # Update _specified_INPUT_Node_InputPorts_in_order with any new agent_rep_input_ports + for i in range(num_agent_rep_input_ports): + if i < len(self._specified_INPUT_Node_InputPorts_in_order): + # Replace existing ones (in case any deferred ones are "placemarked" with None) + self._specified_INPUT_Node_InputPorts_in_order[i] = self.agent_rep_input_ports[i] + else: + # Add any that have been added to Composition + self._specified_INPUT_Node_InputPorts_in_order.append(self.agent_rep_input_ports[i]) if context._execution_phase == ContextFlags.PREPARING: # Restrict validation until run time, when the Composition is expected to be fully constructed self._validate_state_features(context) - def _update_state_input_port_names(self): + self._update_state_input_port_names(context) + + def _update_state_input_port_names(self, context=None): """Update names of state_input_port for any newly instantiated INPUT Node InputPorts + + If its instantiation has NOT been DEFERRED, assert that: + - corresponding agent_rep INPUT Node InputPort is in Composition + - state_input_port either has path_afferents or it is for a numeric spec + + If it's instantiation HAS been DEFERRED, for any newly added agent_rep INPUT Node InputPorts: + - add agent_rep INPUT Node InputPort to _specified_INPUT_Node_InputPorts_in_order + - if state_input_port: + - HAS path_afferents, get source and generate new name + - does NOT have path_afferents, assert it is for a numeric spec and generate new name + - assign new name """ - for i, state_input_port in enumerate(self.state_input_ports): - if i < len(self.agent_rep_input_ports): - self._specified_INPUT_Node_InputPorts_in_order[i] = self.agent_rep_input_ports[i] + num_agent_rep_input_ports = len(self.agent_rep_input_ports) + for i, state_input_port in enumerate(self.state_input_ports): - if i == len(self.agent_rep_input_ports): - # All state_input_ports beyond number of agent_rep_input_ports must be for deferred nodes - assert DEFERRED_STATE_INPUT_PORT_PREFIX in state_input_port.name, \ - f"PROGRAM ERROR: {state_input_port.name} should have 'DEFERRED' in its name." + if context and context.flags & ContextFlags.PREPARING: + # By run time, state_input_port should either have path_afferents assigned or be for a numeric spec + assert state_input_port.path_afferents or NUMERIC_STATE_INPUT_PORT_PREFIX in state_input_port.name, \ + f"PROGRAM ERROR: state_input_port instantiated for '{self.name}' ({state_input_port.name}) " \ + f"with a specification in '{STATE_FEATURES}' ({self.parameters.state_feature_specs.spec[i]}) " \ + f"that is not numeric but has not been assigned any path_afferents." + + if DEFERRED_STATE_INPUT_PORT_PREFIX not in state_input_port.name: + # state_input_port should be associated with existing agent_rep INPUT Node InputPort + assert i < num_agent_rep_input_ports, \ + f"PROGRAM ERROR: state_input_port instantiated for '{self.name}' ({state_input_port.name}) " \ + f"but there is no corresponding INPUT Node in '{AGENT_REP}'." continue - if state_input_port.path_afferents and DEFERRED_STATE_INPUT_PORT_PREFIX in state_input_port.name: - agent_rep_input_port_name = self.agent_rep_input_ports[i].full_name + + if i >= num_agent_rep_input_ports: + # No more new agent_rep INPUT Node InputPorts + break + + # Add new agent_rep INPUT Node InputPorts + self._specified_INPUT_Node_InputPorts_in_order[i] = self.agent_rep_input_ports[i] + agent_rep_input_port_name = self.agent_rep_input_ports[i].full_name + + if state_input_port.path_afferents: + # Non-numeric spec, so get source and change name accordingly source_input_port_name = self.state_feature_specs[i].full_name if 'INPUT FROM' in state_input_port.name: new_name = _state_input_port_name(source_input_port_name, agent_rep_input_port_name) - elif NUMERIC_STATE_INPUT_PORT_PREFIX in state_input_port.name: - new_name = _numeric_state_input_port_name(agent_rep_input_port_name) elif SHADOWED_INPUT_STATE_INPUT_PORT_PREFIX in state_input_port.name: new_name = _shadowed_state_input_port_name(source_input_port_name, agent_rep_input_port_name) - state_input_port.name = rename_instance_in_registry(registry=self._portRegistry, - category=INPUT_PORT, - new_name= new_name, + elif NUMERIC_STATE_INPUT_PORT_PREFIX in state_input_port.name: + # Numeric spec, so change name accordingly + new_name = _numeric_state_input_port_name(agent_rep_input_port_name) + else: + # Non-numeric but path_afferents haven't yet been assigned (will get tested again at run time) + continue + + # Change name of state_input_port + state_input_port.name = rename_instance_in_registry(registry=self._portRegistry, + category=INPUT_PORT, + new_name= new_name, component=state_input_port) def _validate_state_features(self, context): @@ -2848,6 +2889,7 @@ def _instantiate_output_ports(self, context=None): def _instantiate_control_signals(self, context): """Size control_allocation and assign modulatory_signals + Set size of control_allocation equal to number of modulatory_signals. Assign each modulatory_signal sequentially to corresponding item of control_allocation. Assign RANDOMIZATION_CONTROL_SIGNAL for random_variables @@ -3512,7 +3554,6 @@ def state_features(self): (it should be resolved by runtime, or an error is generated). """ - # self._update_state_features_dict() self._update_state_input_port_names() agent_rep_input_ports = self.agent_rep.external_input_ports_of_all_input_nodes diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 629225b6e0d..836454b9c0f 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -3954,11 +3954,10 @@ def _analyze_graph(self, context=None): self._create_CIM_ports(context=context) # Call after above so shadow_projections have relevant organization self._update_shadow_projections(context=context) - # FIX: 12/29/21: MOVE TO _update_shadow_projections - # Call again to accomodate any changes from _update_shadow_projections - self._determine_node_roles(context=context) + # # FIX: 12/29/21 / 3/30/22: MOVE TO _update_shadow_projections + # # Call again to accommodate any changes from _update_shadow_projections + # self._determine_node_roles(context=context) self._check_for_projection_assignments(context=context) - self.needs_update_graph = False def _update_processing_graph(self): @@ -6119,23 +6118,24 @@ def _get_sender_at_right_level(shadowed_proj): f"Composition, neither of which are supported by shadowing.") return original_senders - for shadowing_port, shadowed_port in self.shadowing_dict.items(): - senders = _instantiate_missing_shadow_projections(shadowing_port, - shadowed_port.path_afferents) - for shadow_projection in shadowing_port.path_afferents: - if shadow_projection.sender not in senders: - self.remove_projection(shadow_projection) - Projection_Base._delete_projection(shadow_projection) - if not shadow_projection.sender.efferents: - if isinstance(shadow_projection.sender.owner, CompositionInterfaceMechanism): - ports = shadow_projection.sender.owner.port_map.pop(shadow_projection.receiver) - shadow_projection.sender.owner.remove_ports(list(ports)) - else: - shadow_projection.sender.owner.remove_ports(shadow_projection.sender) + if self.shadowing_dict.items: + for shadowing_port, shadowed_port in self.shadowing_dict.items(): + senders = _instantiate_missing_shadow_projections(shadowing_port, + shadowed_port.path_afferents) + for shadow_projection in shadowing_port.path_afferents: + if shadow_projection.sender not in senders: + self.remove_projection(shadow_projection) + Projection_Base._delete_projection(shadow_projection) + if not shadow_projection.sender.efferents: + if isinstance(shadow_projection.sender.owner, CompositionInterfaceMechanism): + ports = shadow_projection.sender.owner.port_map.pop(shadow_projection.receiver) + shadow_projection.sender.owner.remove_ports(list(ports)) + else: + shadow_projection.sender.owner.remove_ports(shadow_projection.sender) + self._determine_node_roles(context=context) def _check_for_projection_assignments(self, context=None): """Check that all Projections and Ports with require_projection_in_composition attribute are configured. - Validate that all InputPorts with require_projection_in_composition == True have an afferent Projection. Validate that all OutputPorts with require_projection_in_composition == True have an efferent Projection. Validate that all Projections have senders and receivers. diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 8b87a084e10..26169e34515 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -146,7 +146,18 @@ def test_objective_mechanism_spec_as_monitor_for_control_error(self): @pytest.mark.state_features @pytest.mark.parametrize("control_spec", [CONTROL, PROJECTIONS]) - @pytest.mark.parametrize("state_features_arg", ['list','dict']) + @pytest.mark.parametrize("state_features_arg", [ + 'none', + 'default_none', + 'list_none', + 'list_ports', + 'list_reversed', + 'list_numeric', + 'list_partial', + 'dict', + 'dict_reversed', + 'dict_partial', + ]) def test_deferred_init(self, control_spec, state_features_arg): # Test to insure controller works the same regardless of whether it is added to a composition before or after # the nodes it connects to @@ -179,38 +190,81 @@ def test_deferred_init(self, control_spec, state_features_arg): comp = pnl.Composition(name="evc", retain_old_simulation_data=True) state_features = { - 'list': [reward.input_port, Input.input_port], + 'none' : None, + 'default_none' : 'DEFAULT NONE', + 'list_none': [None, None], + 'list_ports': [reward.input_port, Input.input_port], + 'list_reversed': [Input.input_port, reward.input_port], + 'list_partial': [reward.input_port], + 'list_numeric': [[1.1],[2.2]], 'dict': {reward: reward.input_port, - Input: Input.input_port} + Input: Input.input_port}, + 'dict_reversed': {reward: Input.input_port, + Input: reward.input_port}, + 'dict_partial': {reward: reward.input_port} }[state_features_arg] - if state_features_arg == 'list': - expected_warning = "The state_features' arg for 'OptimizationControlMechanism-0' has been specified " \ - "before any Nodes have been assigned to its agent_rep ('evc'). Their order must " \ - "be the same as the order of the corresponding INPUT Nodes for 'evc' once they are " \ - "added, or unexpected results may occur. It is safer to assign all Nodes to the " \ - "agent_rep of a controller before specifying its 'state_features'." + if state_features == 'DEFAULT NONE': + state_feature_default = None + else: + state_feature_default = pnl.SHADOW_INPUTS + + if state_features_arg in {'none', 'default_none', + 'list_none', 'list_ports', 'list_reversed', 'list_numeric', 'list_partial'}: + expected_warning = f"The '{pnl.STATE_FEATURES}' arg for 'OptimizationControlMechanism-0' has been specified " \ + f"before any Nodes have been assigned to its agent_rep ('evc'). Their order must " \ + f"be the same as the order of the corresponding INPUT Nodes for 'evc' once they are " \ + f"added, or unexpected results may occur. It is safer to assign all Nodes to the " \ + f"agent_rep of a controller before specifying its 'state_features'." + elif state_features_arg in {'dict', 'dict_reversed'}: + # expected_warning = f"The 'state_features' specified for 'OptimizationControlMechanism-0' " \ + # f"contains items (Input, reward) that are not in its agent_rep ('evc'). " \ + # f"Executing 'evc' before they are added will generate an error ." + expected_warning = f"that are not in its agent_rep ('evc'). " \ + f"Executing 'evc' before they are added will generate an error ." + elif state_features_arg == 'dict_partial': + expected_warning = f"The '{pnl.STATE_FEATURES}' specified for 'OptimizationControlMechanism-0' " \ + f"contains an item (reward) that is not in its agent_rep ('evc'). " \ + f"Executing 'evc' before it is added will generate an error ." else: - expected_warning = "that are not in its agent_rep ('evc'). Executing 'evc' before they " \ - "are added will generate an error ." + assert False, f"TEST ERROR: unrecognized state_features_arg '{state_features_arg}'" with pytest.warns(UserWarning) as warning: # add the controller to the Composition before adding the relevant Mechanisms - comp.add_controller(controller=pnl.OptimizationControlMechanism( - agent_rep=comp, - state_features = state_features, - state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), - objective_mechanism=pnl.ObjectiveMechanism( - function=pnl.LinearCombination(operation=pnl.PRODUCT), - monitor=[reward, - Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], - (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), - function=pnl.GridSearch(), - control_signals=[{control_spec: ("drift_rate", Decision), - ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, - {control_spec: ("threshold", Decision), - ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) - ) + if 'default_none' in state_features_arg: + comp.add_controller(controller=pnl.OptimizationControlMechanism( + agent_rep=comp, + # state_features = state_features, # Don't specify in order to test default assignments + state_feature_default=state_feature_default, + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), + objective_mechanism=pnl.ObjectiveMechanism( + function=pnl.LinearCombination(operation=pnl.PRODUCT), + monitor=[reward, + Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], + (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), + function=pnl.GridSearch(), + control_signals=[{control_spec: ("drift_rate", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, + {control_spec: ("threshold", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) + ) + else: + comp.add_controller(controller=pnl.OptimizationControlMechanism( + agent_rep=comp, + state_features = state_features, + state_feature_default=state_feature_default, + state_feature_function=pnl.AdaptiveIntegrator(rate=0.5), + objective_mechanism=pnl.ObjectiveMechanism( + function=pnl.LinearCombination(operation=pnl.PRODUCT), + monitor=[reward, + Decision.output_ports[pnl.PROBABILITY_UPPER_THRESHOLD], + (Decision.output_ports[pnl.RESPONSE_TIME], -1, 1)]), + function=pnl.GridSearch(), + control_signals=[{control_spec: ("drift_rate", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}, + {control_spec: ("threshold", Decision), + ALLOCATION_SAMPLES: np.arange(0.1, 1.01, 0.3)}]) + ) assert any(expected_warning in repr(w.message) for w in warning.list) deferred_reward_input_port = _deferred_state_feature_spec_msg('reward[InputPort-0]', 'evc') @@ -219,26 +273,69 @@ def test_deferred_init(self, control_spec, state_features_arg): deferred_node_1 = _deferred_agent_rep_input_port_name('1','evc') deferred_shadowed_0 = _shadowed_state_input_port_name('reward[InputPort-0]' , deferred_node_0) deferred_shadowed_1 = _shadowed_state_input_port_name('Input[InputPort-0]' , deferred_node_1) - deferred_reward_node = _deferred_agent_rep_input_port_name('reward[InputPort-0]','evc') - deferred_Input_node = _deferred_agent_rep_input_port_name('Input[InputPort-0]','evc') - shadowed_reward_node = _shadowed_state_input_port_name('reward[InputPort-0]' ,'reward[InputPort-0]') - shadowed_Input_node = _shadowed_state_input_port_name('Input[InputPort-0]' ,'Input[InputPort-0]') + deferred_shadowed_0_rev = _shadowed_state_input_port_name('Input[InputPort-0]' , deferred_node_0) + deferred_shadowed_1_rev = _shadowed_state_input_port_name('reward[InputPort-0]' , deferred_node_1) + deferred_numeric_input_port_0 = _numeric_state_input_port_name(deferred_node_0) + deferred_numeric_input_port_1 = _numeric_state_input_port_name(deferred_node_1) + deferred_reward_node = _deferred_agent_rep_input_port_name('reward[InputPort-0]', 'evc') + deferred_Input_node = _deferred_agent_rep_input_port_name('Input[InputPort-0]', 'evc') + shadowed_reward_node = _shadowed_state_input_port_name('reward[InputPort-0]', 'reward[InputPort-0]') + shadowed_Input_node = _shadowed_state_input_port_name('Input[InputPort-0]', 'Input[InputPort-0]') + shadowed_reward_node_rev = _shadowed_state_input_port_name('reward[InputPort-0]', 'Input[InputPort-0]') + shadowed_Input_node_rev = _shadowed_state_input_port_name('Input[InputPort-0]', 'reward[InputPort-0]') + numeric_reward_node = _numeric_state_input_port_name('reward[InputPort-0]') + numeric_Input_node = _numeric_state_input_port_name('Input[InputPort-0]') assert comp._controller_initialization_status == pnl.ContextFlags.DEFERRED_INIT - assert comp.controller.state_input_ports.names == [deferred_shadowed_0, deferred_shadowed_1] - if state_features_arg == 'list': + if state_features_arg in {'none', 'default_none'}: + assert comp.controller.state_input_ports.names == [] + assert comp.controller.state_features == {} + assert comp.controller.state_feature_values == {} + elif state_features_arg == 'list_none': + assert comp.controller.state_input_ports.names == [] + assert comp.controller.state_features == {deferred_node_0: None, deferred_node_1: None} + assert comp.controller.state_feature_values == {} + elif state_features_arg == 'list_ports': + assert comp.controller.state_input_ports.names == [deferred_shadowed_0, deferred_shadowed_1] assert comp.controller.state_features == {deferred_node_0: deferred_reward_input_port, deferred_node_1: deferred_Input_input_port} assert comp.controller.state_feature_values == {deferred_node_0: deferred_reward_input_port, deferred_node_1: deferred_Input_input_port} + elif state_features_arg == 'list_reversed': + assert comp.controller.state_input_ports.names == [deferred_shadowed_0_rev, deferred_shadowed_1_rev] + assert comp.controller.state_features == {deferred_node_0: deferred_Input_input_port, + deferred_node_1: deferred_reward_input_port} + assert comp.controller.state_feature_values == {deferred_node_0: deferred_Input_input_port, + deferred_node_1: deferred_reward_input_port} + elif state_features_arg == 'list_partial': + assert comp.controller.state_input_ports.names == [deferred_shadowed_0] + assert comp.controller.state_features == {deferred_node_0: deferred_reward_input_port} + assert comp.controller.state_feature_values == {deferred_node_0: deferred_reward_input_port} + elif state_features_arg == 'list_numeric': + assert comp.controller.state_input_ports.names == [deferred_numeric_input_port_0, + deferred_numeric_input_port_1] + assert comp.controller.state_features == {deferred_node_0: [1.1], deferred_node_1: [2.2]} + assert np.allclose(list(comp.controller.state_feature_values.values()), [[0.9625],[1.925]]) + assert list(comp.controller.state_feature_values.keys()) == [deferred_node_0, deferred_node_1] elif state_features_arg == 'dict': + assert comp.controller.state_input_ports.names == [deferred_shadowed_0, deferred_shadowed_1] assert comp.controller.state_features == {deferred_reward_node: deferred_reward_input_port, deferred_Input_node: deferred_Input_input_port} assert comp.controller.state_feature_values == {deferred_reward_node: deferred_reward_input_port, deferred_Input_node: deferred_Input_input_port} + elif state_features_arg == 'dict_reversed': + assert comp.controller.state_input_ports.names == [deferred_shadowed_0_rev, deferred_shadowed_1_rev] + assert comp.controller.state_features == {deferred_reward_node: deferred_Input_input_port, + deferred_Input_node: deferred_reward_input_port} + assert comp.controller.state_feature_values == {deferred_reward_node: deferred_Input_input_port, + deferred_Input_node: deferred_reward_input_port} + elif state_features_arg == 'dict_partial': + assert comp.controller.state_input_ports.names == [deferred_shadowed_0] + assert comp.controller.state_features == {deferred_reward_node: deferred_reward_input_port} + assert comp.controller.state_feature_values == {deferred_reward_node: deferred_reward_input_port} else: - assert False, f"TEST ERROR: unrecognized option '{state_features_arg}'" + assert False, f"TEST ERROR: unrecognized state_features_arg '{state_features_arg}'" comp.add_node(reward, required_roles=[pnl.NodeRole.OUTPUT]) comp.add_node(Decision, required_roles=[pnl.NodeRole.OUTPUT]) @@ -246,11 +343,37 @@ def test_deferred_init(self, control_spec, state_features_arg): comp.add_linear_processing_pathway(task_execution_pathway) comp.enable_controller = True - assert comp.controller.state_features == {'reward[InputPort-0]': 'reward[InputPort-0]', - 'Input[InputPort-0]': 'Input[InputPort-0]'} - assert comp.controller.state_feature_values == {reward.input_port: [0.], Input.input_port: [0.]} - assert all(p.path_afferents for p in comp.controller.state_input_ports) - assert comp.controller.state_input_ports.names == [shadowed_reward_node, shadowed_Input_node] + + if state_features_arg == 'default_none': + assert not any(p.path_afferents for p in comp.controller.state_input_ports) + assert comp.controller.state_features == {} + assert comp.controller.state_feature_values == {} + assert comp.controller.state_input_ports.names == [] + elif state_features_arg == 'list_none': + assert not any(p.path_afferents for p in comp.controller.state_input_ports) + assert comp.controller.state_features == {'reward[InputPort-0]': None, + 'Input[InputPort-0]': None} + assert comp.controller.state_feature_values == {} + assert comp.controller.state_input_ports.names == [] + elif state_features_arg == 'list_numeric': + assert not any(p.path_afferents for p in comp.controller.state_input_ports) + assert comp.controller.state_input_ports.names == [numeric_reward_node, numeric_Input_node] + assert comp.controller.state_features == {'reward[InputPort-0]': [1.1], + 'Input[InputPort-0]': [2.2]} + assert np.allclose(list(comp.controller.state_feature_values.values()), [[1.065625],[2.13125]]) + assert list(comp.controller.state_feature_values.keys()) == [reward.input_port, Input.input_port] + elif state_features_arg in {'list_reversed', 'dict_reversed'}: + assert all(p.path_afferents for p in comp.controller.state_input_ports) + assert comp.controller.state_features == {'reward[InputPort-0]': 'Input[InputPort-0]', + 'Input[InputPort-0]': 'reward[InputPort-0]'} + assert comp.controller.state_feature_values == {Input.input_port: [0], reward.input_port: [0]} + assert comp.controller.state_input_ports.names == [shadowed_Input_node_rev, shadowed_reward_node_rev] + else: + assert all(p.path_afferents for p in comp.controller.state_input_ports) + assert comp.controller.state_features == {'reward[InputPort-0]': 'reward[InputPort-0]', + 'Input[InputPort-0]': 'Input[InputPort-0]'} + assert comp.controller.state_feature_values == {reward.input_port: [0], Input.input_port: [0]} + assert comp.controller.state_input_ports.names == [shadowed_reward_node, shadowed_Input_node] # comp._analyze_graph() @@ -261,41 +384,143 @@ def test_deferred_init(self, control_spec, state_features_arg): comp.run(inputs=stim_list_dict) - # Note: Removed decision variable OutputPort from simulation results because sign is chosen randomly - expected_sim_results_array = [ - [[10.], [10.0], [0.0], [0.48999867], [0.50499983]], - [[10.], [10.0], [0.0], [1.08965888], [0.51998934]], - [[10.], [10.0], [0.0], [2.40680493], [0.53494295]], - [[10.], [10.0], [0.0], [4.43671978], [0.549834]], - [[10.], [10.0], [0.0], [0.48997868], [0.51998934]], - [[10.], [10.0], [0.0], [1.08459402], [0.57932425]], - [[10.], [10.0], [0.0], [2.36033556], [0.63645254]], - [[10.], [10.0], [0.0], [4.24948962], [0.68997448]], - [[10.], [10.0], [0.0], [0.48993479], [0.53494295]], - [[10.], [10.0], [0.0], [1.07378304], [0.63645254]], - [[10.], [10.0], [0.0], [2.26686573], [0.72710822]], - [[10.], [10.0], [0.0], [3.90353015], [0.80218389]], - [[10.], [10.0], [0.0], [0.4898672], [0.549834]], - [[10.], [10.0], [0.0], [1.05791834], [0.68997448]], - [[10.], [10.0], [0.0], [2.14222978], [0.80218389]], - [[10.], [10.0], [0.0], [3.49637662], [0.88079708]], - [[15.], [15.0], [0.0], [0.48999926], [0.50372993]], - [[15.], [15.0], [0.0], [1.08981011], [0.51491557]], - [[15.], [15.0], [0.0], [2.40822035], [0.52608629]], - [[15.], [15.0], [0.0], [4.44259627], [0.53723096]], - [[15.], [15.0], [0.0], [0.48998813], [0.51491557]], - [[15.], [15.0], [0.0], [1.0869779], [0.55939819]], - [[15.], [15.0], [0.0], [2.38198336], [0.60294711]], - [[15.], [15.0], [0.0], [4.33535807], [0.64492386]], - [[15.], [15.0], [0.0], [0.48996368], [0.52608629]], - [[15.], [15.0], [0.0], [1.08085171], [0.60294711]], - [[15.], [15.0], [0.0], [2.32712843], [0.67504223]], - [[15.], [15.0], [0.0], [4.1221271], [0.7396981]], - [[15.], [15.0], [0.0], [0.48992596], [0.53723096]], - [[15.], [15.0], [0.0], [1.07165729], [0.64492386]], - [[15.], [15.0], [0.0], [2.24934228], [0.7396981]], - [[15.], [15.0], [0.0], [3.84279648], [0.81637827]] - ] + if state_features_arg in {'default_none', 'list_none'}: + expected_sim_results_array = [ + [[0.], [0.], [0.], [0.49], [0.5]], + [[0.], [0.], [0.], [1.09], [0.5]], + [[0.], [0.], [0.], [2.41], [0.5]], + [[0.], [0.], [0.], [4.45], [0.5]], + [[0.], [0.], [0.], [0.49], [0.5]], + [[0.], [0.], [0.], [1.09], [0.5]], + [[0.], [0.], [0.], [2.41], [0.5]], + [[0.], [0.], [0.], [4.45], [0.5]], + [[0.], [0.], [0.], [0.49], [0.5]], + [[0.], [0.], [0.], [1.09], [0.5]], + [[0.], [0.], [0.], [2.41], [0.5]], + [[0.], [0.], [0.], [4.45], [0.5]], + [[0.], [0.], [0.], [0.49], [0.5]], + [[0.], [0.], [0.], [1.09], [0.5]], + [[0.], [0.], [0.], [2.41], [0.5]], + [[0.], [0.], [0.], [4.45], [0.5]], + [[0.], [0.], [0.], [0.49], [0.5]], + [[0.], [0.], [0.], [1.09], [0.5]], + [[0.], [0.], [0.], [2.41], [0.5]], + [[0.], [0.], [0.], [4.45], [0.5]], + [[0.], [0.], [0.], [0.49], [0.5]], + [[0.], [0.], [0.], [1.09], [0.5]], + [[0.], [0.], [0.], [2.41], [0.5]], + [[0.], [0.], [0.], [4.45], [0.5]], + [[0.], [0.], [0.], [0.49], [0.5]], + [[0.], [0.], [0.], [1.09], [0.5]], + [[0.], [0.], [0.], [2.41], [0.5]], + [[0.], [0.], [0.], [4.45], [0.5]], + [[0.], [0.], [0.], [0.49], [0.5]], + [[0.], [0.], [0.], [1.09], [0.5]], + [[0.], [0.], [0.], [2.41], [0.5]], + [[0.], [0.], [0.], [4.45], [0.5]]] + elif state_features_arg == 'list_numeric': + expected_sim_results_array = [ + [[1.09785156], [1.09785156], [0.], [0.48989747], [0.5438015]], + [[1.09946289], [1.09946289], [0.], [1.06483807], [0.66899791]], + [[1.09986572], [1.09986572], [0.], [2.19475384], [0.77414214]], + [[1.09996643], [1.09996643], [0.], [3.66103375], [0.85320293]], + [[1.09999161], [1.09999161], [0.], [0.48842594], [0.66907284]], + [[1.0999979], [1.0999979], [0.], [0.85321354], [0.94353405]], + [[1.09999948], [1.09999948], [0.], [1.23401798], [0.99281107]], + [[1.09999987], [1.09999987], [0.], [1.58437432], [0.99912464]], + [[1.09999997], [1.09999997], [0.], [0.48560629], [0.77416842]], + [[1.09999999], [1.09999999], [0.], [0.70600576], [0.99281108]], + [[1.1], [1.1], [0.], [0.90438208], [0.99982029]], + [[1.1], [1.1], [0.], [1.09934486], [0.99999554]], + [[1.1], [1.1], [0.], [0.48210997], [0.85320966]], + [[1.1], [1.1], [0.], [0.63149987], [0.99912464]], + [[1.1], [1.1], [0.], [0.76817898], [0.99999554]], + [[1.1], [1.1], [0.], [0.90454543], [0.99999998]], + [[1.1], [1.1], [0.], [0.48989707], [0.54388677]], + [[1.1], [1.1], [0.], [1.06481464], [0.66907403]], + [[1.1], [1.1], [0.], [2.19470819], [0.77416843]], + [[1.1], [1.1], [0.], [3.66099691], [0.85320966]], + [[1.1], [1.1], [0.], [0.48842592], [0.66907403]], + [[1.1], [1.1], [0.], [0.85321303], [0.94353433]], + [[1.1], [1.1], [0.], [1.23401763], [0.99281108]], + [[1.1], [1.1], [0.], [1.58437418], [0.99912464]], + [[1.1], [1.1], [0.], [0.48560629], [0.77416843]], + [[1.1], [1.1], [0.], [0.70600576], [0.99281108]], + [[1.1], [1.1], [0.], [0.90438208], [0.99982029]], + [[1.1], [1.1], [0.], [1.09934486], [0.99999554]], + [[1.1], [1.1], [0.], [0.48210997], [0.85320966]], + [[1.1], [1.1], [0.], [0.63149987], [0.99912464]], + [[1.1], [1.1], [0.], [0.76817898], [0.99999554]], + [[1.1], [1.1], [0.], [0.90454543], [0.99999998]]] + elif state_features_arg in {'list_reversed', 'dict_reversed'}: + expected_sim_results_array = [ + [[0.25], [0.25], [0.], [0.4879949], [0.68997448]], + [[0.25], [0.25], [0.], [0.81866742], [0.96083428]], + [[0.25], [0.25], [0.], [1.14484206], [0.99631576]], + [[0.25], [0.25], [0.], [1.4493293], [0.99966465]], + [[0.25], [0.25], [0.], [0.47304171], [0.96083428]], + [[0.25], [0.25], [0.], [0.54999945], [0.99999724]], + [[0.25], [0.25], [0.], [0.625], [1.]], + [[0.25], [0.25], [0.], [0.7], [1.]], + [[0.25], [0.25], [0.], [0.46418045], [0.99631576]], + [[0.25], [0.25], [0.], [0.50714286], [1.]], + [[0.25], [0.25], [0.], [0.55], [1.]], + [[0.25], [0.25], [0.], [0.59285714], [1.]], + [[0.25], [0.25], [0.], [0.45999329], [0.99966465]], + [[0.25], [0.25], [0.], [0.49], [1.]], + [[0.25], [0.25], [0.], [0.52], [1.]], + [[0.25], [0.25], [0.], [0.55], [1.]], + [[0.1865], [0.1865], [0.], [0.4858033], [0.76852478]], + [[0.1865], [0.1865], [0.], [0.7123133], [0.99183743]], + [[0.1865], [0.1865], [0.], [0.91645684], [0.99977518]], + [[0.1865], [0.1865], [0.], [1.11665847], [0.99999386]], + [[0.1865], [0.1865], [0.], [0.46639458], [0.99183743]], + [[0.1865], [0.1865], [0.], [0.51666667], [1.]], + [[0.1865], [0.1865], [0.], [0.56666667], [1.]], + [[0.1865], [0.1865], [0.], [0.61666667], [1.]], + [[0.1865], [0.1865], [0.], [0.45951953], [0.99977518]], + [[0.1865], [0.1865], [0.], [0.48809524], [1.]], + [[0.1865], [0.1865], [0.], [0.51666667], [1.]], + [[0.1865], [0.1865], [0.], [0.5452381], [1.]], + [[0.1865], [0.1865], [0.], [0.45666658], [0.99999386]], + [[0.1865], [0.1865], [0.], [0.47666667], [1.]], + [[0.1865], [0.1865], [0.], [0.49666667], [1.]], + [[0.1865], [0.1865], [0.], [0.51666667], [1.]]] + else: + # Note: Removed decision variable OutputPort from simulation results because sign is chosen randomly + expected_sim_results_array = [ + [[10.], [10.0], [0.0], [0.48999867], [0.50499983]], + [[10.], [10.0], [0.0], [1.08965888], [0.51998934]], + [[10.], [10.0], [0.0], [2.40680493], [0.53494295]], + [[10.], [10.0], [0.0], [4.43671978], [0.549834]], + [[10.], [10.0], [0.0], [0.48997868], [0.51998934]], + [[10.], [10.0], [0.0], [1.08459402], [0.57932425]], + [[10.], [10.0], [0.0], [2.36033556], [0.63645254]], + [[10.], [10.0], [0.0], [4.24948962], [0.68997448]], + [[10.], [10.0], [0.0], [0.48993479], [0.53494295]], + [[10.], [10.0], [0.0], [1.07378304], [0.63645254]], + [[10.], [10.0], [0.0], [2.26686573], [0.72710822]], + [[10.], [10.0], [0.0], [3.90353015], [0.80218389]], + [[10.], [10.0], [0.0], [0.4898672], [0.549834]], + [[10.], [10.0], [0.0], [1.05791834], [0.68997448]], + [[10.], [10.0], [0.0], [2.14222978], [0.80218389]], + [[10.], [10.0], [0.0], [3.49637662], [0.88079708]], + [[15.], [15.0], [0.0], [0.48999926], [0.50372993]], + [[15.], [15.0], [0.0], [1.08981011], [0.51491557]], + [[15.], [15.0], [0.0], [2.40822035], [0.52608629]], + [[15.], [15.0], [0.0], [4.44259627], [0.53723096]], + [[15.], [15.0], [0.0], [0.48998813], [0.51491557]], + [[15.], [15.0], [0.0], [1.0869779], [0.55939819]], + [[15.], [15.0], [0.0], [2.38198336], [0.60294711]], + [[15.], [15.0], [0.0], [4.33535807], [0.64492386]], + [[15.], [15.0], [0.0], [0.48996368], [0.52608629]], + [[15.], [15.0], [0.0], [1.08085171], [0.60294711]], + [[15.], [15.0], [0.0], [2.32712843], [0.67504223]], + [[15.], [15.0], [0.0], [4.1221271], [0.7396981]], + [[15.], [15.0], [0.0], [0.48992596], [0.53723096]], + [[15.], [15.0], [0.0], [1.07165729], [0.64492386]], + [[15.], [15.0], [0.0], [2.24934228], [0.7396981]], + [[15.], [15.0], [0.0], [3.84279648], [0.81637827]]] for simulation in range(len(expected_sim_results_array)): assert np.allclose(expected_sim_results_array[simulation], @@ -956,10 +1181,10 @@ def test_args_specific_to_ocm(self, id, agent_rep, state_features, monitor_for_c ('comp_in_list_spec', pnl.SHADOW_INPUTS, messages[10], pnl.OptimizationControlMechanismError), ('comp_in_shadow_inupts_spec', pnl.SHADOW_INPUTS, messages[11], pnl.OptimizationControlMechanismError) ] - if len(state_feature_args) != 23: - print("\n\n**********************************************************************************************") - print("*** RESTORE state_feature_args IN test_ocm_state_feature_specs_and_warnings_and_errors() *****") - print("***********************************************************************************************") + if len(state_feature_args) != 27: + print("\n\n************************************************************************************************") + print("*** UNCOMMENT state_feature_args IN test_ocm_state_feature_specs_and_warnings_and_errors() *****") + print("************************************************************************************************") @pytest.mark.state_features @pytest.mark.control @pytest.mark.parametrize('state_feature_args', state_feature_args, ids=[x[0] for x in state_feature_args]) diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index a5a07507c74..65836803fcb 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -399,19 +399,19 @@ def test_nested_learning(self, show_graph_kwargs, expected_output): ), ( {'show_nested': False, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF TARGET[InputPort-0] FOR TARGET[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]SHADOWED INPUT OF TARGET[InputPort-0] FOR TARGET[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': False, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=orange penwidth=1]\n\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF TARGET[InputPort-0] FOR TARGET[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]SHADOWED INPUT OF TARGET[InputPort-0] FOR TARGET[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ( {'show_nested': False, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" [color=pink penwidth=3 rank=same shape=rectangle]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION" [label="" arrowhead=normal color=black penwidth=1]\n\t"NESTED COMPOSITION" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF TARGET[InputPort-0] FOR TARGET[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]SHADOWED INPUT OF TARGET[InputPort-0] FOR TARGET[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n}' ), ( {'show_nested': NESTED, 'show_cim': True, 'show_node_structure': True, 'show_learning': True}, - 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-Shadowed input of TARGET[InputPort-0]]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]Shadowed input of TARGET[InputPort-0]]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' + 'digraph COMPOSITION {\n\tgraph [label=COMPOSITION overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tTARGET [label=<
OutputPort-0
OutputPorts
Mechanism:
TARGET
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t"OUTER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tINTERNAL [label=<
OutputPort-0
OutputPorts
Mechanism:
INTERNAL
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=black penwidth=1 rank=same shape=plaintext]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> INTERNAL:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tINTERNAL:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_INNER INPUT_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tTARGET:"OutputPort-OutputPort-0" -> "NESTED COMPOSITION INPUT_CIM":"InputPort-INPUT_CIM_Target_InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"NESTED COMPOSITION OUTPUT_CIM":"OutputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" -> "OUTER OUTPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_OUTER INPUT_InputPort-0INPUT_CIM_TARGET_InputPort-0
OutputPorts
Mechanism:
COMPOSITION Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> "OUTER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> TARGET:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t"COMPOSITION OUTPUT_CIM" [label=<
Mechanism:
COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_OUTER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_OUTER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\tCONTROLLER:"OutputPort-INTERNAL[slope] ControlSignal" -> INTERNAL:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1 style=solid]\n\t"OBJECTIVE MECHANISM" [label=<
OUTCOME
OutputPorts
Mechanism:
OBJECTIVE MECHANISM
ParameterPorts
offset
scale
InputPorts
Value of OUTER INPUT [OutputPort-0]Value of OUTER OUTPUT [OutputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\t"OBJECTIVE MECHANISM":"OutputPort-OUTCOME" -> CONTROLLER:"InputPort-OUTCOME" [label="" color=purple penwidth=1]\n\t"OUTER INPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER INPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"OUTER OUTPUT":"OutputPort-OutputPort-0" -> "OBJECTIVE MECHANISM":"InputPort-Value of OUTER OUTPUT [OutputPort-0]" [label="" color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_OUTER INPUT_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_TARGET_InputPort-0" -> CONTROLLER:"InputPort-SHADOWED INPUT OF TARGET[InputPort-0] FOR TARGET[InputPort-0]" [label="" arrowhead=normal color=purple penwidth=1]\n\t"OUTER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
OUTER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tCONTROLLER [label=<
INTERNAL[slope] ControlSignal
OutputPorts
Mechanism:
CONTROLLER
ParameterPorts
seed
InputPorts
OUTCOMESHADOWED INPUT OF OUTER INPUT[InputPort-0] FOR OUTER INPUT[InputPort-0]SHADOWED INPUT OF TARGET[InputPort-0] FOR TARGET[InputPort-0]
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph "cluster_NESTED COMPOSITION" {\n\t\tgraph [label="NESTED COMPOSITION" 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\tTarget [label=<
OutputPort-0
OutputPorts
Mechanism:
Target
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=orange penwidth=3 rank=min shape=plaintext]\n\t\t"INNER INPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER INPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [color=black penwidth=1 shape=diamond]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [arrowhead=none color=black penwidth=1]\n\t\t"MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" -> "INNER OUTPUT":"InputPort-InputPort-0" [color=black penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"OutputPort-LearningSignal" -> "MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label="" color=orange penwidth=1]\n\t\t"NESTED COMPOSITION INPUT_CIM" [label=<
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Input_CIM
InputPorts
INPUT_CIM_INNER INPUT_InputPort-0INPUT_CIM_Target_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_INNER INPUT_InputPort-0" -> "INNER INPUT":"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION INPUT_CIM":"OutputPort-INPUT_CIM_Target_InputPort-0" -> Target:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\t"NESTED COMPOSITION OUTPUT_CIM" [label=<
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
OutputPorts
Mechanism:
NESTED COMPOSITION Output_CIM
InputPorts
OUTPUT_CIM_INNER OUTPUT_OutputPort-0
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "NESTED COMPOSITION OUTPUT_CIM":"InputPort-OUTPUT_CIM_INNER OUTPUT_OutputPort-0" [label="" arrowhead=normal color=black penwidth=1 style=solid]\n\t\tComparator [label=<
OUTCOMEMSE
OutputPorts
Mechanism:
Comparator
ParameterPorts
offset
scale
InputPorts
SAMPLETARGET
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> Comparator:"InputPort-SAMPLE" [label="" arrowhead=normal color=orange penwidth=1]\n\t\tTarget:"OutputPort-OutputPort-0" -> Comparator:"InputPort-TARGET" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]" [label=<
error_signalLearningSignal
OutputPorts
Mechanism:
Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]
ParameterPorts
learning_rate
InputPorts
activation_inputactivation_outputerror_signal
> color=orange penwidth=1 rank=min shape=plaintext]\n\t\tComparator:"OutputPort-OUTCOME" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-error_signal" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER INPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_input" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT":"OutputPort-OutputPort-0" -> "Learning Mechanism for MappingProjection from INNER INPUT[OutputPort-0] to INNER OUTPUT[InputPort-0]":"InputPort-activation_output" [label="" arrowhead=normal color=orange penwidth=1]\n\t\t"INNER OUTPUT" [label=<
OutputPort-0
OutputPorts
Mechanism:
INNER OUTPUT
ParameterPorts
intercept
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tlabel="NESTED COMPOSITION"\n\t}\n}' ), ]