diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 1f5e2b863d7..6ef3bad7875 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -3475,6 +3475,22 @@ def log_values(self, entries): """ self.log.log_values(entries) + def _propagate_most_recent_context(self, context=None, visited=None): + if visited is None: + visited = set([self]) + + if context is None: + context = self.most_recent_context + + self.most_recent_context = context + + # TODO: avoid duplicating objects in _dependent_components + # throughout psyneulink or at least condense these methods + for obj in self._dependent_components: + if obj not in visited: + visited.add(obj) + obj._propagate_most_recent_context(context, visited) + @property def _dict_summary(self): from psyneulink.core.compositions.composition import Composition diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 63f4e42eb96..e35a9eab1be 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1105,7 +1105,7 @@ class `UserList ` of the Mechanism. @@ -2530,23 +2532,28 @@ def execute(self, if not self.parameters.execute_until_finished._get(context): break - # REPORT EXECUTION if called from command line - # If called by a Composition, it handles reporting. - if context.source == ContextFlags.COMMAND_LINE: - # FIX: 3/11/21 THIS SHOULD BE REFACTORED TO USE Report.report_output rather than printing directly - from psyneulink.core.compositions.report import ReportOutput - if (self.prefs.reportOutputPref is not ReportOutput.OFF - and (context.execution_phase & ContextFlags.PROCESSING | ContextFlags.LEARNING)): - from psyneulink.core.compositions.report import Report - from rich import print - if self.prefs.reportOutputPref is ReportOutput.TERSE: - print(f'{self.name} executed') - else: - print(Report.node_execution_report(self, - input_val=self.get_input_values(context), - output_val=self.output_port.parameters.value._get(context), - report_output=True, - context=context)) + # REPORT EXECUTION + + if (context.source == ContextFlags.COMMAND_LINE or + context.execution_phase & (ContextFlags.PROCESSING | ContextFlags.LEARNING)): + from psyneulink.core.compositions.report import Report, ReportOutput, ReportParams + # Use report_output and report_params options passed to execute from Composition or command line; + # otherwise try to get from Mechanism's reportOutputPref + report_output = report_output or next((pref for pref in convert_to_list(self.prefs.reportOutputPref) + if isinstance(pref, ReportOutput)), None) + report_params = report_params or next((pref for pref in convert_to_list(self.prefs.reportOutputPref) + if isinstance(pref, ReportParams)), None) + if report_output is not ReportOutput.OFF: + with Report(self, context=context) as report: + report.report_output(caller=self, + report_num=run_report, + scheduler=None, + report_output=report_output, + report_params=report_params, + content='node', + context=context, + node=self) + return value def _get_variable_from_input(self, input, context=None): diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index d3c943e73db..d89cf6810c4 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -882,18 +882,18 @@ .. _Composition_Execution_Reporting: -*Reporting*. Executing a Composition returns the results of its last `TRIAL ` of execution. If -either `run ` or `learn ` is called, the results of all `TRIALS ` -executed are available in the Composition's `results ` attribute (see `Results -` for additional details). A report of the results of each -`TRIAL ` can also be generated as the Compostion is executing, using the **report_output** and -**report_progress** arguments of any of the execution methods. **report_output** generates a report of the input and -output the Composition and its `Nodes `, while **report_progress** shows a progress bar indicating -how many `TRIALS ` have been executed and an estimate of the time remaining to completion (see the -`execute `, `run ` and `learn ` methods for additional -details). These options are both False by default. The values of individual Components (and their `parameters -`) assigned during execution can also be recorded in their `log ` attribute using the -`Log` facility. +*Results, Reporting and Logging*. Executing a Composition returns the results of its last `TRIAL ` of +execution. If either `run ` or `learn ` is called, the results of all `TRIALS +` executed are available in the Composition's `results ` attribute (see `Results +` for additional details). A report of the results of each `TRIAL ` +can also be generated as the Compostion is executing, using the **report_output** and **report_progress** arguments +of any of the execution methods. **report_output** (specified using `ReportOutput` options) generates a report of the +input and output of the Composition and its `Nodes `, and optionally their `Parameters` (specified +in the **report_params** arg using `ReportParams` options); **report_progress** (specified using `ReportProgress` +options) shows a progress bar indicating how many `TRIALS ` have been executed and an estimate of +the time remaining to completion. These options are all OFF by default (see `Report` for additional details). +The values of individual Components (and their `parameters `) assigned during execution can also be +recorded in their `log ` attribute using the `Log` facility. *Inputs*. All methods of executing a Composition require specification of an **inputs** argument, which designates the values assigned to the `INPUT` `Nodes ` of the Composition for each `TRIAL `. @@ -2394,12 +2394,13 @@ def input_function(env, result): from psyneulink.core.components.projections.projection import ProjectionError, DuplicateProjectionError from psyneulink.core.components.shellclasses import Composition_Base from psyneulink.core.components.shellclasses import Mechanism, Projection -from psyneulink.core.compositions.report import Report, ReportOutput, ReportProgress, ReportSimulations -from psyneulink.core.compositions.showgraph import ShowGraph, INITIAL_FRAME, SHOW_CIM, EXECUTION_SET +from psyneulink.core.compositions.report import Report,\ + ReportOutput, ReportParams, ReportProgress, ReportSimulations, ReportDevices +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, ANY, BEFORE, COMPONENT, COMPOSITION, CONTROLLER, CONTROL_SIGNAL, DEFAULT, \ - FEEDBACK, FULL, HARD_CLAMP, IDENTITY_MATRIX, INPUT, INPUT_PORTS, INPUTS, INPUT_CIM_NAME, LEARNED_PROJECTIONS, \ + FEEDBACK, 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, \ @@ -7457,14 +7458,15 @@ def evaluate( execution_mode=execution_mode, skip_initialization=True, report_output=report._report_output, + report_params=report._report_params, report_progress=report._report_progress, report_simulations=report._report_simulations, report_to_devices=report._report_to_devices ) - context.remove_flag(ContextFlags.SIMULATION_MODE) - context.execution_phase = ContextFlags.CONTROL - if buffer_animate_state: - self._animate = buffer_animate_state + context.remove_flag(ContextFlags.SIMULATION_MODE) + context.execution_phase = ContextFlags.CONTROL + if buffer_animate_state: + self._animate = buffer_animate_state # Store simulation results on "base" composition if self.initialization_status != ContextFlags.INITIALIZING: @@ -8023,6 +8025,7 @@ def run( termination_processing=None, skip_analyze_graph=False, report_output:ReportOutput=ReportOutput.OFF, + report_params:ReportParams=ReportParams.OFF, report_progress=ReportProgress.OFF, report_simulations=ReportSimulations.OFF, report_to_devices=None, @@ -8127,6 +8130,10 @@ def run( specifies whether to show output of the Composition and its `Nodes ` trial-by-trial as it is generated; see `Report_Output` for additional details and `ReportOutput` for options. + report_params : ReportParams : default ReportParams.OFF + specifies whether to show values the `Parameters` of the Composition and its `Nodes ` + as part of the output report; see `Report_Output` for additional details and `ReportParams` for options. + report_progress : ReportProgress : default ReportProgress.OFF specifies whether to report progress of execution in real time; see `Report_Progress` for additional details. @@ -8397,9 +8404,9 @@ def run( # copies back matrix to pnl from param struct (after learning) _comp_ex._copy_params_to_pnl(context=context) + self._propagate_most_recent_context(context) # KAM added the [-1] index after changing Composition run() # behavior to return only last trial of run (11/7/18) - self.most_recent_context = context return results[-1] except Exception as e: @@ -8418,12 +8425,13 @@ def run( with Report(self, report_output=report_output, + report_params=report_params, report_progress=report_progress, report_simulations=report_simulations, report_to_devices=report_to_devices, context=context) as report: - progress_report = report.start_progress_report(self, num_trials, context) + run_report = report.start_run_report(self, num_trials, context) # Loop over the length of the list of inputs - each input represents a TRIAL for trial_num in range(num_trials): @@ -8461,10 +8469,11 @@ def run( skip_initialization=True, execution_mode=execution_mode, report_output=report_output, + report_params=report_params, report_progress=report_progress, report_simulations=report_simulations, report=report, - progress_report=progress_report + run_report=run_report ) # --------------------------------------------------------------------------------- @@ -8494,9 +8503,6 @@ def run( if call_after_trial: call_with_pruned_args(call_after_trial, context=context) - # Report results to output devices - report.report_output(self, progress_report, scheduler, report_output, 'run', context) - if report._recorded_reports: self.recorded_reports = report._recorded_reports if report._rich_diverted_reports: @@ -8505,8 +8511,8 @@ def run( # IMPLEMENTATION NOTE: # The AFTER Run controller execution takes place here, because there's no way to tell from within the execute # method whether or not we are at the last trial of the run. - # The BEFORE Run controller execution takes place in the execute method,, because we can't execute the controller until after - # setup has occurred for the Input CIM. + # The BEFORE Run controller execution takes place in the execute method, + # because we can't execute the controller until after setup has occurred for the Input CIM. if (self.controller_mode == AFTER and self.controller_time_scale == TimeScale.RUN): try: @@ -8516,6 +8522,7 @@ def run( self._execute_controller( execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) @@ -8634,17 +8641,21 @@ def learn( called after each minibatch is executed report_output : ReportOutput : default ReportOutput.OFF - specifies whether to show output of the Composition and its `Nodes ` trial-by-trial as - it is generated; see `Report_Output` for additional details and `ReportOutput` for options. + specifies whether to show output of the Composition and its `Nodes ` trial-by-trial + as it is generated; see `Report_Output` for additional details and `ReportOutput` for options. + + report_params : ReportParams : default ReportParams.OFF + specifies whether to show values the `Parameters` of the Composition and its `Nodes ` + as part of the output report; see `Report_Output` for additional details and `ReportParams` for options. report_progress : ReportProgress : default ReportProgress.OFF specifies whether to report progress of execution in real time; see `Report_Progress` for additional details. report_simulations : ReportSimulatons : default ReportSimulations.OFF - specifies whether to show output and/or progress for `simulations ` - executed by the Composition's `controller `; see `Report_Simulations` for - additional details. + specifies whether to show output and/or progress for `simulations + ` executed by the Composition's `controller + `; see `Report_Simulations` for additional details. report_to_devices : list(ReportDevices) : default ReportDevices.CONSOLE specifies where output and progress should be reported; see `Report_To_Devices` for additional @@ -8699,7 +8710,13 @@ def learn( context.remove_flag(ContextFlags.LEARNING_MODE) return learning_results - def _execute_controller(self, relative_order=AFTER, execution_mode=False, _comp_ex=False, context=None): + def _execute_controller(self, + relative_order=AFTER, + execution_mode=False, + _comp_ex=False, + report=None, + context=None + ): execution_scheduler = context.composition.scheduler if (self.enable_controller and self.controller_mode == relative_order and @@ -8713,6 +8730,9 @@ def _execute_controller(self, relative_order=AFTER, execution_mode=False, _comp_ self.initialization_status != ContextFlags.INITIALIZING and ContextFlags.SIMULATION_MODE not in context.runmode ): + + report._execution_stack.append(self.controller) + if self.controller and not execution_mode: # FIX: REMOVE ONCE context IS SET TO CONTROL ABOVE # FIX: END REMOVE @@ -8720,7 +8740,7 @@ def _execute_controller(self, relative_order=AFTER, execution_mode=False, _comp_ self.controller.execute(context=context) if execution_mode: - _comp_ex.execute_node(self.controller) + _comp_ex.execute_node(self.controller, context=context) context.remove_flag(ContextFlags.PROCESSING) @@ -8730,6 +8750,8 @@ def _execute_controller(self, relative_order=AFTER, execution_mode=False, _comp_ self._animate_execution(self.controller, context) context.remove_flag(ContextFlags.CONTROL) + report._execution_stack.pop() + @handle_external_context(execution_phase=ContextFlags.PROCESSING) def execute( self, @@ -8748,11 +8770,12 @@ def execute( skip_initialization=False, execution_mode:pnlvm.ExecutionMode = pnlvm.ExecutionMode.Python, report_output:ReportOutput=ReportOutput.OFF, - report_progress=ReportProgress.OFF, - report_simulations=ReportSimulations.OFF, - report_to_devices=None, + report_params:ReportOutput=ReportParams.OFF, + report_progress:ReportProgress=ReportProgress.OFF, + report_simulations:ReportSimulations=ReportSimulations.OFF, + report_to_devices:ReportDevices=None, report=None, - progress_report=None, + run_report=None, ): """ Passes inputs to any `Nodes ` receiving inputs directly from the user (via the "inputs" @@ -8817,6 +8840,10 @@ def execute( specifies whether to show output of the Composition and its `Nodes ` for the execution; see `Report_Output` for additional details and `ReportOutput` for options. + report_params : ReportParams : default ReportParams.OFF + specifies whether to show values the `Parameters` of the Composition and its `Nodes ` + for the execution; see `Report_Output` for additional details and `ReportParams` for options. + report_progress : ReportProgress : default ReportProgress.OFF specifies whether to report progress of the execution; see `Report_Progress` for additional details. @@ -8837,15 +8864,16 @@ def execute( with Report(self, report_output=report_output, + report_params=report_params, report_progress=report_progress, report_simulations=report_simulations, report_to_devices=report_to_devices, context=context) as report: - # FIX: Call Report with context and progress_report handle this in there 3/3/21 + # FIX: Call Report with context and run_report handle this in there 3/3/21 # If execute method is called directly, need to create Report object for reporting - if not (context.source & ContextFlags.COMPOSITION) or progress_report is None: - progress_report = report.start_progress_report(comp=self, num_trials=1, context=context) + if not (context.source & ContextFlags.COMPOSITION) or run_report is None: + run_report = report.start_run_report(comp=self, num_trials=1, context=context) execution_scheduler = scheduler or self.scheduler @@ -8926,7 +8954,7 @@ def execute( else: assert False, "Unknown execution mode: {}".format(execution_mode) - report.report_progress(self, progress_report, context) + report.report_progress(self, run_report, context) # If called from the command line, get report as only this trial is run if context.source & ContextFlags.COMMAND_LINE: if report._recorded_reports: @@ -8934,6 +8962,7 @@ def execute( if report._rich_diverted_reports: self.rich_diverted_reports = report._rich_diverted_reports + self._propagate_most_recent_context(context) return _comp_ex.extract_node_output(self.output_CIM) except Exception as e: @@ -8981,7 +9010,7 @@ def execute( reset_stateful_functions_to = {} # # Report trial_num and Composition input (now that it has been assigned) - # progress.report_output(self, progress_report, execution_scheduler, report_output, 'trial_init', context) + # progress.report_output(self, run_report, execution_scheduler, report_output, 'trial_init', context) for node in self.nodes: node.parameters.num_executions.get(context)._set_by_time_scale(TimeScale.TRIAL, 0) @@ -9027,7 +9056,7 @@ def execute( build_CIM_input = self._build_variable_for_input_CIM(inputs) if execution_mode: - _comp_ex.execute_node(self.input_CIM, inputs) + _comp_ex.execute_node(self.input_CIM, inputs, context) # FIXME: parameter_CIM should be executed here as well, # but node execution of nested compositions with # outside control is not supported yet. @@ -9091,6 +9120,7 @@ def execute( relative_order=BEFORE, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) elif self.controller_time_scale == TimeScale.TRIAL: @@ -9098,6 +9128,7 @@ def execute( relative_order=BEFORE, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) @@ -9120,6 +9151,7 @@ def execute( relative_order=BEFORE, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) @@ -9133,8 +9165,15 @@ def execute( for i in range(scheduler.clock.time.time_step): execution_sets.__next__() - # Report trial_num and Composition input (now that it has been assigned) - report.report_output(self, progress_report, execution_scheduler, report_output, 'trial_init', context) + # Add TRIAL header and Composition's input to output report (now that they are known) + report.report_output(caller=self, + report_num=run_report, + scheduler=execution_scheduler, + report_output=report_output, + report_params=report_params, + content='trial_init', + context=context + ) for next_execution_set in execution_sets: @@ -9159,6 +9198,7 @@ def execute( relative_order=AFTER, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) next_pass_after += 1 @@ -9171,6 +9211,7 @@ def execute( relative_order=BEFORE, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) next_pass_before += 1 @@ -9183,6 +9224,7 @@ def execute( relative_order=BEFORE, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) @@ -9201,11 +9243,12 @@ def execute( if not self._is_learning(context): next_execution_set = next_execution_set - set(self.get_nodes_by_role(NodeRole.LEARNING)) - # INITIALIZE self._time_step_report AND SHOW TIME_STEP DIVIDER + # Add TIME_STEP header to output report nodes_to_report = any(node.reportOutputPref for node in next_execution_set) - report.report_output(self, progress_report, + report.report_output(self, run_report, execution_scheduler, report_output, + report_params, 'time_step_init', context, nodes_to_report=True) @@ -9267,7 +9310,7 @@ def execute( # Execute Mechanism if execution_mode: - _comp_ex.execute_node(node) + _comp_ex.execute_node(node, context=context) else: if node is not self.controller: mech_context = copy(context) @@ -9276,6 +9319,9 @@ def execute( for port in node.input_ports: port._update(context=context) node.execute(context=mech_context, + report_output=report_output, + report_params=report_params, + run_report=run_report, runtime_params=execution_runtime_params, ) # Reset runtim_params @@ -9303,6 +9349,8 @@ def execute( elif isinstance(node, Composition): + report._execution_stack.append(node) + if execution_mode: # Invoking nested composition passes data via Python # structures. Make sure all sources get their latest values @@ -9340,6 +9388,7 @@ def execute( pnlvm.ExecutionMode.Python ret = node.execute(context=context, report_output=report_output, + report_params=report_params, report_progress=report_progress, execution_mode=nested_execution_mode) @@ -9353,18 +9402,22 @@ def execute( context.composition = self + report._execution_stack.pop() + + # Add Node info for TIME_STEP to output report + report.report_output(self, + run_report, + execution_scheduler, + report_output, + report_params, + 'node', + context, + node=node) + # ANIMATE node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if self._animate is not False and self._animate_unit == COMPONENT: self._animate_execution(node, context) - # Add report for node to time_step_report - report.report_output(self, - progress_report, - execution_scheduler, - report_output, - 'node', - context, - node=node) # MANAGE INPUTS (for next execution_set)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -9396,6 +9449,7 @@ def execute( relative_order=AFTER, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) @@ -9403,9 +9457,10 @@ def execute( call_with_pruned_args(call_after_time_step, context=context) - # Add report for time_step to trial_report - report.report_output(self, progress_report, execution_scheduler, report_output, 'time_step', context, - nodes_to_report= nodes_to_report) + # Complete TIME_STEP entry for output report + report.report_output(self, run_report, execution_scheduler, + report_output, report_params, 'time_step', context, + nodes_to_report= nodes_to_report) context.remove_flag(ContextFlags.PROCESSING) @@ -9428,6 +9483,7 @@ def execute( relative_order=AFTER, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) @@ -9438,28 +9494,27 @@ def execute( self._animate_execution(self.output_CIM, context) # FIX: END + # EXECUTE CONTROLLER (if controller_mode == AFTER) ********************************************************* if self.controller_time_scale == TimeScale.TRIAL: self._execute_controller( relative_order=AFTER, execution_mode=execution_mode, _comp_ex=_comp_ex, + report=report, context=context ) - # REPORT RESULTS ******************************************************************************************* - # Extract result here if execution_mode: _comp_ex.freeze_values() - _comp_ex.execute_node(self.output_CIM) - report.report_progress(self, progress_report, context) + _comp_ex.execute_node(self.output_CIM, context=context) + report.report_progress(self, run_report, context) if context.source & ContextFlags.COMMAND_LINE: if report._recorded_reports: self.recorded_reports = report._recorded_reports if report._rich_diverted_reports: self.rich_diverted_reports = report._rich_diverted_reports - return _comp_ex.extract_node_output(self.output_CIM) # Reset context flags @@ -9472,14 +9527,10 @@ def execute( for port in self.output_CIM.output_ports: output_values.append(port.parameters.value._get(context)) - # Report results and progress to output devices - report.report_output(self, progress_report, execution_scheduler, report_output, 'trial', context) - report.report_progress(self, progress_report, context) - if context.source & ContextFlags.COMMAND_LINE: - if report._recorded_reports: - self.recorded_reports = report._recorded_reports - if report._rich_diverted_reports: - self.rich_diverted_reports = report._rich_diverted_reports + # Complete TRIAL entry for output report, and report progress + report.report_output(self, run_report, execution_scheduler, report_output, + report_params, 'trial', context) + report.report_progress(self, run_report, context) # UPDATE TIME and RETURN *********************************************************************************** diff --git a/psyneulink/core/compositions/report.py b/psyneulink/core/compositions/report.py index fdb99bf93ef..9824c04fcbb 100644 --- a/psyneulink/core/compositions/report.py +++ b/psyneulink/core/compositions/report.py @@ -15,7 +15,7 @@ .. _Report_Output: -Output reporting +Output Reporting ---------------- Output reporting provides information about the input and output to a `Mechanism` or to a `Composition` and @@ -24,15 +24,18 @@ Mechanism's `execute ` method or any of a Composition's `execution methods `. If `USE_PREFS ` or `TERSE ` is used, reporting is generated as execution of each Component occurs; if `FULL ` is used, then the -information is reported at the end of each `TRIAL ` executed. Whether `simulations +information is reported at the end of each `TRIAL ` executed. This always includes the input and +output to a `Mechanism` or a `Composition` and its `Nodes `, and can also include the values +of their `Parameters`, depending on the specification of the **report_params** argument (using `ReportParams` options` +and/or the `reportOutputPref ` settings of individual Mechanisms. Whether `simulations ` executed by a Composition's `controller ` are included is determined by the **report_simulations** argument using a `ReportSimulations` option. Output is reported -to the devices specified in the **report_to_devices** argument using the `ReportDevices` options +to the devices specified in the **report_to_devices** argument using the `ReportDevices` options. .. _Report_Progress: -Progress reporting +Progress Reporting ------------------ Progress reporting provides information about the status of execution of a Composition's `run ` @@ -48,7 +51,7 @@ included is determined by the **report_simulations** argument using a `ReportSimulations` option. Progress is reported to the devices specified in the **report_to_devices** argument using the `ReportDevices` options. -.. _technical_note:: +.. technical_note:: Progress reporting is generated and displayed using a `rich Progress Display `_ object. @@ -87,6 +90,7 @@ from enum import Enum, Flag, auto from io import StringIO +import numpy as np from rich import print, box from rich.console import Console, RenderGroup from rich.panel import Panel @@ -94,15 +98,19 @@ from psyneulink.core.globals.context import Context from psyneulink.core.globals.context import ContextFlags -from psyneulink.core.globals.keywords import FUNCTION_PARAMS, INPUT_PORTS, OUTPUT_PORTS +from psyneulink.core.globals.keywords import FUNCTION_PARAMS, INPUT_PORTS, OUTPUT_PORTS, VALUE from psyneulink.core.globals.utilities import convert_to_list -__all__ = ['Report', 'ReportOutput', 'ReportProgress', 'ReportDevices', 'CONSOLE', 'RECORD', 'DIVERT', 'PNL_VIEW'] +__all__ = ['Report', 'ReportOutput', 'ReportParams', 'ReportProgress', 'ReportDevices', 'ReportSimulations', + 'CONSOLE', 'CONTROLLED', 'LOGGED', 'MODULATED', 'RECORD', 'DIVERT', 'PNL_VIEW', ] SIMULATION = 'Simulat' DEFAULT = 'Execut' SIMULATIONS = 'simulations' +SIMULATING = 'simulating' REPORT_REPORT = False # USED FOR DEBUGGING +OUTPUT_REPORT = 'output_report' +PROGRESS_REPORT = 'progress_report' # rich console report styles # node @@ -125,7 +133,7 @@ class ReportOutput(Enum): ` or the `execute ` method of a `Mechanism`, to enable and determine the type of output generated by reporting; see `Report_Output` for additional details. - .. _technical_note:: + .. technical_note:: Use of these options is expected in the **report_output** constructor for the `Report` object, and are used as the values of its `report_output ` attribute. @@ -139,7 +147,7 @@ class ReportOutput(Enum): use the `reportOutputPref ` of each `Composition` and/or `Mechanism` executed to determine whether and in what format to report its execution. - TERSE + TERSE (aka ON) enforce reporting execution of *all* Compositions and/or Mechanisms as they are executed, irrespective of their `reportOutputPref ` settings, using a simple line-by-line format to report each. @@ -148,23 +156,76 @@ class ReportOutput(Enum): `TRIAL ` of execution, including the input and output of each, irrespective of their `reportOutputPref ` settings. - .. _technical_note:: + .. technical_note:: Output is formatted using `rich Panel objects `_. """ OFF = 0 USE_PREFS = 1 TERSE = 2 + ON = 2 FULL = 3 +class ReportParams(Enum): + """ + Options used in the **report_params** argument of a `Composition`\'s `execution methods + `, to specify the scope of reporting for its `Parameters` and those of its + `Nodes `; see `Reporting Parameter values ` under `Report_Output` for + additional details. + + .. technical_note:: + Use of these options is expected in the **report_output** constructor for the `Report` object, + and are used as the values of its `report_params ` attribute. + + Attributes + ---------- + + OFF + suppress reporting of parameter values. + + USE_PREFS + defers to `reportOutputPref ` settings of individual Components. + + MODULATED (aka CONTROLLED) + report all `Parameters` that are being `modulated ` (i.e., controlled) by a + `ControlMechanism` within the `Composition` (that is, those for which the corresponding `ParameterPort` + receives a `ControlProjection` from a `ControlSignal`. + + CONTROLLED (aka MODULATED) + report all `Parameters` that are being controlled (i.e., `modulated `) by a + `ControlMechanism` within the `Composition` (that is, those for which the corresponding `ParameterPort` + receives a `ControlProjection` from a `ControlSignal`. + + LOGGED + report all `Parameters` that are specified to be logged with `LogCondition.EXECUTION`; see `Log` for + additional details. + + ALL + enforce reporting of all `Parameters` of a `Composition` and its `Nodes `. + + """ + + OFF = 0 + MODULATED = auto() + CONTROLLED = MODULATED + MONITORED = auto() + LOGGED = auto() + ALL = auto() + +MODULATED = ReportParams.MODULATED +CONTROLLED = ReportParams.CONTROLLED +LOGGED = ReportParams.LOGGED +ALL = ReportParams.ALL + + class ReportProgress(Enum): """ Options used in the **report_progress** argument of a `Composition`\'s `run ` and `learn ` methods, to enable/disable progress reporting during execution of a Composition; see `Report_Progress` for additional details. - .. _technical_note:: + .. technical_note:: Use of these options is expected in the **report_progress** constructor for the `Report` object, and are used as the values of its `report_progress ` attribute. @@ -188,7 +249,7 @@ class ReportSimulations(Enum): ` methods, to specify whether `simulations ` executed by an a Composition's `controller ` are included in output and progress reporting. - .. _technical_note:: + .. technical_note:: Use of these options is expected in the **report_progress** constructor for the `Report` object, and are used as the values of its `report_progress ` attribute. @@ -212,7 +273,7 @@ class ReportDevices(Flag): ` or the `execute ` method of a `Mechanism`, to determine the devices to which reporting is directed. - .. _technical_note:: + .. technical_note:: Use of these options is expected in the **report_to_devices** constructor for the `Report` object, and are used as the values of its `_report_to_devices ` attribute. @@ -222,7 +283,7 @@ class ReportDevices(Flag): CONSOLE direct reporting to the console in which PsyNeuLink is running - .. _technical_note:: + .. technical_note:: output is rendered using the `Console markup `_ by a `rich Progress `_ object stored in `_instance._rich_progress `. @@ -232,7 +293,7 @@ class ReportDevices(Flag): option on its own replaces and suppresses reporting to the console; to continue to generate console output, explicitly include `CONSOLE` along with `RECORD` in the argument specification. - .. _technical_note:: + .. technical_note:: DIVERT capture reporting otherwise directed to the rich Console in a UDF-8 formatted string and stores it in `_rich_diverted_reports `. This option suppresses @@ -265,7 +326,7 @@ def __str__(self): return repr(self.error_value) -class ProgressReport(): +class RunReport(): """ Object used to package Progress reporting for a call to the `run ` or `learn ` methods of a `Composition`. @@ -291,6 +352,9 @@ class Report: specifies whether to report output of the execution on a trial-by-trial as it is generated; see `ReportOutput` for options. + _report_params : list[ReportParams] : default [ReportParams.USE_PREFS] + specifies which params are reported if ReportOutput.FULL is in effect. + report_progress : ReportProgress : default ReportProgress.OFF specifies whether to report progress of each `TRIAL ` of a `Composition`\\'s execution, showing the number of `TRIALS ` that have been executed and a progress bar; see @@ -300,7 +364,7 @@ class Report: specifies whether to report output and progress for `simulations ` executed by an a Composition's `controller `; see `ReportSimulations` for options. - report_to_devices : list(ReportDevices) : default ReportDevices.CONSOLE + report_to_devices : list[ReportDevices] : default [ReportDevices.CONSOLE] specifies devices to which output and progress reporting is sent; see `ReportDevices` for options. Attributes @@ -316,18 +380,21 @@ class Report: identifies whether reporting is enabled; True if either the **_report_output** or **_report_progress** progress arguments of the constructor were specified as not False. - _report_output : bool, *TERSE*, or *FULL* : default False + _report_output : ReportOutput : default ReportOutput.OFF determines whether and, if so, what form of output is displayed and/or captured. - _report_progress : bool : default False + _report_params : list[ReportParams] : default [ReportParams.USE_PREFS] + determines which params are reported if ReportOutput.FULL is in effect. + + _report_progress : ReportProgress : default ReportProgress.OFF determines whether progress is displayed and/or captured. - _report_simulations : bool : default False + _report_simulations : ReportSimulations : default ReportSimulations.OFF determines whether reporting occurs for output and/or progress of `simulations ` carried out by the `controller ` of a `Composition`. - _report_to_devices : list + _report_to_devices : list[ReportDevices : [ReportDevices.CONSOLE] list of devices currently enabled for reporting. _use_rich : False, *CONSOLE*, *DIVERT* or list: default *CONSOLE* @@ -354,26 +421,45 @@ class Report: _recorded_reports : str : default [] if _record_reports is True, contains a record of reports generated during execution. - _progress_reports : dict + _recording_enabled : bool : default False + True if any device is specified other than `CONSOLE `. + + _run_reports : dict contains entries for each Composition (the key) executed during progress reporting; the value of each entry is itself a dict with two entries: - - one containing ProgressReports for executions in DEFAULT_MODE (key: DEFAULT) - - one containing ProgressReports for executions in SIMULATION_MODE (key: SIMULATION) + - one containing RunReports for executions in DEFAULT_MODE (key: DEFAULT) + - one containing RunReports for executions in SIMULATION_MODE (key: SIMULATION) + + _execution_stack : list : default [] + tracks `nested compositions ` and `controllers ` + (i.e., being used to `simulate ` a `Composition`). + + _execution_stack_depth : int : default 0 + depth of nesting of executions, including `nested compositions ` and any `controllers + ` currently executing `simulations `. + + _nested_comps : bool : default False + True if there are any `nested compositions ` + in `_execution_stack `. + + _simulating : bool : default False + True if there are any `controllers ` + in `_execution_stack `. _ref_count : int : default 0 tracks how many times object has been referenced; counter is incremented on each context __enter__ and decrements on each __exit__, to ensure stop progress is not called until all references have been released. - """ _instance = None def __new__(cls, caller, - report_output:ReportOutput=False, + report_output:ReportOutput=ReportOutput.OFF, + report_params:ReportParams=ReportParams.OFF, report_progress:ReportProgress=ReportProgress.OFF, report_simulations:ReportSimulations=ReportSimulations.OFF, - report_to_devices:(list(ReportDevices.__members__), list)=ReportDevices, + report_to_devices:(list(ReportDevices.__members__), list)=ReportDevices.CONSOLE, context:Context=None ) -> 'Report': if cls._instance is None: @@ -401,20 +487,22 @@ def __new__(cls, # Assign option properties cls._report_progress = report_progress cls._report_output = report_output + cls._report_params = convert_to_list(report_params) cls._reporting_enabled = report_output is not ReportOutput.OFF or cls._report_progress cls._report_simulations = report_simulations cls._rich_console = ReportDevices.CONSOLE in cls._report_to_devices cls._rich_divert = ReportDevices.DIVERT in cls._report_to_devices cls._record_reports = ReportDevices.RECORD in cls._report_to_devices + cls._recording_enabled = any(i is not ReportDevices.CONSOLE for i in cls._report_to_devices) # Enable rich if reporting output or progress and using console or recording cls._use_rich = (cls._reporting_enabled and (cls._rich_console or cls._rich_divert or cls._record_reports)) cls._use_pnl_view = ReportDevices.PNL_VIEW in cls._report_to_devices - cls._prev_simulation = False + cls._execution_stack = [caller] # Instantiate rich progress context object - # - it is not started until the self.start_progress_report() method is called + # - it is not started until the self.start_run_report() method is called # - auto_refresh is disabled to accommodate IDEs (such as PyCharm and Jupyter Notebooks) if cls._use_rich: # Set up RECORDING @@ -430,7 +518,7 @@ def __new__(cls, if cls._use_pnl_view: warnings.warn("'pnl_view' not yet supported as an option for report_progress of Composition.run()") - cls._progress_reports = {} + cls._run_reports = {} cls._recorded_reports = str() cls._rich_diverted_reports = str() @@ -490,9 +578,9 @@ def __exit__(self, type, value, traceback) -> None: # bar will grow and grow and never be deallocated until the end of program. Report._destroy() - def start_progress_report(self, comp, num_trials, context) -> int: + def start_run_report(self, comp, num_trials, context) -> int: """ - Initialize a ProgressReport for Composition + Initialize a RunReport for Composition Arguments --------- @@ -509,8 +597,8 @@ def start_progress_report(self, comp, num_trials, context) -> int: Returns ------- - ProgressReport id : int - id is stored in `progress_reports `. + RunReport id : int + id is stored in `_run_reports `. """ @@ -521,11 +609,11 @@ def start_progress_report(self, comp, num_trials, context) -> int: # Generate space before beginning of output - if self._use_rich and not self._progress_reports: + if self._use_rich and not self._run_reports: print() - if comp not in self._progress_reports: - self._progress_reports.update({comp:{DEFAULT:[], SIMULATION:[]}}) + if comp not in self._run_reports: + self._run_reports.update({comp:{DEFAULT:[], SIMULATION:[], SIMULATING:False}}) # Used for accessing progress report and reporting results if context.runmode & ContextFlags.SIMULATION_MODE: @@ -536,16 +624,18 @@ def start_progress_report(self, comp, num_trials, context) -> int: if run_mode is SIMULATION and self._report_simulations is not ReportSimulations.ON: return - # Don't create a new report for simulations in a set - if run_mode is SIMULATION and self._prev_simulation: - return len(self._progress_reports[comp][run_mode]) - 1 + if run_mode is SIMULATION: + # If already simulating, return existing report for those simulations + if self._run_reports[comp][SIMULATING]: + return len(self._run_reports[comp][run_mode]) - 1 if self._use_rich: - # visible = self._report_progress and (run_mode is not SIMULATION or self._report_simulations) visible = (self._rich_console + # progress reporting is ON and self._report_progress is ReportProgress.ON - and (run_mode is not SIMULATION or self._report_simulations is not ReportSimulations.ON) + # current run is not a simulation (being run by a controller), or simulation reporting is ON + and (not self._simulating or self._report_simulations is ReportSimulations.ON) ) if comp.verbosePref or REPORT_REPORT: @@ -559,23 +649,29 @@ def start_progress_report(self, comp, num_trials, context) -> int: else: start = True - id = self._rich_progress.add_task(f"[red]{run_mode}ing {comp.name}...", + indent_factor = 2 + depth_indent = depth_str = '' + if run_mode is SIMULATION or self._execution_stack_depth: + depth_indent = indent_factor * self._execution_stack_depth * ' ' + depth_str = f' (depth: {self._execution_stack_depth})' + + id = self._rich_progress.add_task(f"[red]{depth_indent}{comp.name}: {run_mode}ing {depth_str}...", total=num_trials, start=start, visible=visible ) - self._progress_reports[comp][run_mode].append(ProgressReport(id, num_trials)) - report_num = len(self._progress_reports[comp][run_mode]) - 1 + self._run_reports[comp][run_mode].append(RunReport(id, num_trials)) + report_num = len(self._run_reports[comp][run_mode]) - 1 - self._prev_simulation = run_mode is SIMULATION + self._run_reports[comp][SIMULATING] = run_mode is SIMULATION return report_num - def report_progress(self, caller, report_num, context): + def report_progress(self, caller, report_num:int, context:Context): """ Report progress of executions in call to `run ` or `learn ` method of - a `Composition`. + a `Composition`, and record reports if specified. Arguments --------- @@ -583,7 +679,7 @@ def report_progress(self, caller, report_num, context): caller : Composition or Mechanism report_num : int - id of ProgressReport for caller[run_mode] in self._progress_reports to use for reporting. + id of RunReport for caller[run_mode] in self._run_reports to use for reporting. context : Context context providing information about run_mode (DEFAULT or SIMULATION). @@ -595,47 +691,62 @@ def report_progress(self, caller, report_num, context): simulation_mode = context.runmode & ContextFlags.SIMULATION_MODE if simulation_mode: run_mode = SIMULATION + # Return if (nested within) a simulation and not reporting simulations + if self._report_simulations is ReportSimulations.OFF: + return else: run_mode = DEFAULT - # Return if (nested within) a simulation and not reporting simulations - if run_mode is SIMULATION and self._report_simulations is not ReportSimulations.ON: - return - - progress_report = self._progress_reports[caller][run_mode][report_num] - trial_num = self._rich_progress.tasks[progress_report.rich_task_id].completed + run_report = self._run_reports[caller][run_mode][report_num] + trial_num = self._rich_progress.tasks[run_report.rich_task_id].completed # Useful for debugging: if caller.verbosePref or REPORT_REPORT: from pprint import pprint pprint(f'{caller.name} {str(context.runmode)} REPORT') + # Not in simulation and have reached num_trials (if specified), + if self._run_reports[caller][SIMULATING] and not simulation_mode: + # If was simulating previously, then have just exited, so: + # (note: need to use transition and not explicit count of simulations, + # since number of simulation trials being run is generally not known) + # - turn it off + self._run_reports[caller][SIMULATING] = False + # Update progress report if self._use_rich: - if progress_report.num_trials: + if run_report.num_trials: if simulation_mode: num_trials_str = '' else: - num_trials_str = f' of {progress_report.num_trials}' + num_trials_str = f' of {run_report.num_trials}' else: num_trials_str = '' - update = f'{caller.name}: {run_mode}ed {trial_num+1}{num_trials_str} trials' - self._rich_progress.update(progress_report.rich_task_id, + # Construct update text + indent_factor = 2 + depth_indent = depth_str = '' + if simulation_mode or self._execution_stack_depth: + depth_indent = indent_factor * self._execution_stack_depth * ' ' + depth_str = f' (depth: {self._execution_stack_depth})' + update = f'{depth_indent}{caller.name}: {run_mode}ed {trial_num+1}{num_trials_str} trials{depth_str}' + + # Do update + self._rich_progress.update(run_report.rich_task_id, description=update, advance=1, refresh=True) - # track number of outer (non-simulation) trials - if (not simulation_mode - and progress_report.num_trials - and (trial_num == progress_report.num_trials)): - self._progress_reports[caller][run_mode].pop() + if self._report_progress is not ReportProgress.OFF: + self._print_and_record_reports(PROGRESS_REPORT, context) - def report_output(self, caller, + + def report_output(self, + caller, report_num:int, scheduler, report_output:ReportOutput, + report_params:list, content:str, context:Context, nodes_to_report:bool=False, @@ -647,8 +758,11 @@ def report_output(self, caller, Arguments --------- + caller : Composition or Mechanism + Component requesting report; used to identify relevant run_report. + report_num : int - specifies id of `ProgressReport`, stored in `_progress_reports ` for each + specifies id of `RunReport`, stored in `_run_reports ` for each Composition executed and mode of execution (DEFAULT or SIMULATION). scheduler : Scheduler @@ -660,6 +774,11 @@ def report_output(self, caller, `execution method ` or a Mechanism's `execute ` method. + report_params : [ReportParams] + conveys `ReportParams` option(s) specified in the **report_params** argument of the call to a Composition's + `execution method ` or a Mechanism's `execute ` + method. + content : str specifies content of current element of report; must be: 'trial_init', 'time_step_init', 'node', 'time_step', 'trial' or 'run'. @@ -677,54 +796,71 @@ def report_output(self, caller, specifies `node ` for which output is being reported. """ - if report_num is None or report_output is ReportOutput.OFF: + # if report_num is None or report_output is ReportOutput.OFF: + if report_output is ReportOutput.OFF: return + # Determine report type and relevant parameters ---------------------------------------------------------------- + + # Get ReportOutputPref for node if node: node_pref = next((pref for pref in convert_to_list(node.reportOutputPref) if isinstance(pref, ReportOutput)), None) - # Assign trial_report_type and node_report_type + # Assign report_output as default for trial_report_type and node_report_type... trial_report_type = node_report_type = report_output + + # then try to get them from caller, based on whether it is a Mechanism or Composition from psyneulink.core.compositions.composition import Composition from psyneulink.core.components.mechanisms.mechanism import Mechanism # Report is called for by a Mechanism if isinstance(caller, Mechanism): + if context.source & ContextFlags.COMPOSITION: + run_report_owner = context.composition + trial_report_type=report_output # FULL output reporting doesn't make sense for a Mechanism, since it includes trial info, so enforce TERSE - trial_report_type = ReportOutput.TERSE + else: + trial_report_type = None # If USE_PREFS is specified by user, then assign output format to Mechanism's reportOutputPref if report_output is ReportOutput.USE_PREFS: node_report_type = node_pref - # USE_PREFS is specified for report called by a Composition: - elif isinstance(caller, Composition) and report_output is ReportOutput.USE_PREFS: - # First, if report is for execution of a node, assign its report type using its reportOutputPref: - if node: - # Get ReportOutput spec from reportOutputPref if there is one - # If None was found, assign ReportOutput.FULL as default - node_report_type = node_pref or ReportOutput.FULL - # Return if it is OFF - if node_report_type is ReportOutput.OFF: + if node_pref is ReportOutput.OFF: return + elif isinstance(caller, Composition): + # USE_PREFS is specified for report called by a Composition: + if report_output is ReportOutput.USE_PREFS: + # First, if report is for execution of a node, assign its report type using its reportOutputPref: + if node: + # Get ReportOutput spec from reportOutputPref if there is one + # If None was found, assign ReportOutput.FULL as default + node_report_type = node_pref or ReportOutput.FULL + # Return if it is OFF + if node_report_type is ReportOutput.OFF: + return + trial_num = scheduler.clock.time.trial + run_report_owner = caller + + # Determine run_mode and get run_report if call is from a Composition or a Mechanism being executed by one + if isinstance(caller, Composition) or context.source == ContextFlags.COMPOSITION: + simulation_mode = context.runmode & ContextFlags.SIMULATION_MODE + if simulation_mode and self._report_simulations is ReportSimulations.OFF: + return + if simulation_mode: + run_mode = SIMULATION + sim_str = ' SIMULATION' + else: + run_mode = DEFAULT + sim_str = '' + run_report = self._run_reports[run_report_owner][run_mode][report_num] - simulation_mode = context.runmode & ContextFlags.SIMULATION_MODE - if simulation_mode: - run_mode = SIMULATION - sim_str = ' SIMULATION' - else: - run_mode = DEFAULT - sim_str = '' - - progress_report = self._progress_reports[caller][run_mode][report_num] - - trial_num = scheduler.clock.time.trial + # Construct output report ----------------------------------------------------------------------------- if content is 'trial_init': - - progress_report.trial_report = [] + run_report.trial_report = [] # if FULL output, report trial number and Composition's input # note: header for Trial Panel is constructed under 'content is Trial' case below if trial_report_type is ReportOutput.FULL: - progress_report.trial_report = [f"\n[bold {trial_panel_color}]input:[/]" + run_report.trial_report = [f"\n[bold {trial_panel_color}]input:[/]" f" {[i.tolist() for i in caller.get_input_values(context)]}"] else: # TERSE output # print trial title and separator + input array to Composition @@ -736,7 +872,7 @@ def report_output(self, caller, elif content is 'time_step_init': if trial_report_type is ReportOutput.FULL: - progress_report.time_step_report = [] # Contains rich.Panel for each node executed in time_step + run_report.time_step_report = [] # Contains rich.Panel for each node executed in time_step elif nodes_to_report: # TERSE output time_step_header = f'[{time_step_panel_color}] Time Step {scheduler.clock.time.time_step} ---------' self._rich_progress.console.print(time_step_header) @@ -750,11 +886,12 @@ def report_output(self, caller, input_val=node.get_input_values(context), output_val=node.output_port.parameters.value._get(context), report_output=node_report_type, + report_params=report_params, context=context ) - # If trial is using FULL report, save Node's to progress_report + # If trial is using FULL report, save Node's to run_report if trial_report_type is ReportOutput.FULL: - progress_report.time_step_report.append(node_report) + run_report.time_step_report.append(node_report) # Otherwise, just print it to the console (as part of otherwise TERSE report) else: # TERSE output self._rich_progress.console.print(node_report) @@ -765,8 +902,8 @@ def report_output(self, caller, elif content is 'time_step': if nodes_to_report and trial_report_type is ReportOutput.FULL: - progress_report.trial_report.append('') - progress_report.trial_report.append(Panel(RenderGroup(*progress_report.time_step_report), + run_report.trial_report.append('') + run_report.trial_report.append(Panel(RenderGroup(*run_report.time_step_report), # box=box.HEAVY, border_style=time_step_panel_color, box=time_step_panel_box, @@ -779,66 +916,81 @@ def report_output(self, caller, output_values = [] for port in caller.output_CIM.output_ports: output_values.append(port.parameters.value._get(context)) - progress_report.trial_report.append(f"\n[bold {trial_output_color}]result:[/]" + run_report.trial_report.append(f"\n[bold {trial_output_color}]result:[/]" f" {[r.tolist() for r in output_values]}\n") - progress_report.trial_report = Panel(RenderGroup(*progress_report.trial_report), + run_report.trial_report = Panel(RenderGroup(*run_report.trial_report), box=trial_panel_box, border_style=trial_panel_color, title=f'[bold{trial_panel_color}] {caller.name}{sim_str}: ' f'Trial {trial_num} [/]', expand=False) - if context.source & ContextFlags.COMMAND_LINE and trial_report_type is not ReportOutput.OFF: - self._print_reports(progress_report) - elif content is 'run': - self._print_reports(progress_report) + if trial_report_type is not ReportOutput.OFF: + self._print_and_record_reports(OUTPUT_REPORT, context, run_report) else: assert False, f"Bad 'content' argument in call to Report.report_output() for {caller.name}: {content}." return - def _print_reports(self, progress_report): + def _print_and_record_reports(self, report_type:str, context:Context, run_report:RunReport=None): """ Conveys output reporting to device specified in `_report_to_devices `. - Called by `report_output ` + Called by `report_output ` and `report_progress ` Arguments --------- - progress_report : int - id of ProgressReport for caller[run_mode] in self._progress_reports to use for reporting. + run_report : int + id of RunReport for caller[run_mode] in self._run_reports to use for reporting. """ - # if self._rich_console and progress_report.trial_report: - if (self._rich_console or self._rich_divert) and progress_report.trial_report: - self._rich_progress.console.print(progress_report.trial_report) - self._rich_progress.console.print('') - update = '\n'.join([t.description for t in self._rich_progress.tasks]) - if self._report_output: - if self._rich_divert: - self._rich_diverted_reports += (f'\n{self._rich_progress.console.file.getvalue()}') - if self._record_reports: - with self._recording_console.capture() as capture: - self._recording_console.print(progress_report.trial_report) - self._recorded_reports += capture.get() - if self._report_progress is ReportProgress.ON: + # Print and record output report as they are created (progress reports are printed by _rich_progress.console) + if report_type is OUTPUT_REPORT: + # Print output reports as they are created + if (self._rich_console or self._rich_divert) and run_report.trial_report: + self._rich_progress.console.print(run_report.trial_report) + self._rich_progress.console.print('') + # Record output reports as they are created + if len(self._execution_stack)==1 and self._report_output is not ReportOutput.OFF: + if self._rich_divert: + self._rich_diverted_reports += (f'\n{self._rich_progress.console.file.getvalue()}') + if self._record_reports: + with self._recording_console.capture() as capture: + self._recording_console.print(run_report.trial_report) + self._recorded_reports += capture.get() + + # Record progress after execution of outer-most Composition + if len(self._execution_stack)==1: + if report_type is PROGRESS_REPORT: + # add progress report to any already recorded for output + progress_reports = '\n'.join([t.description for t in self._rich_progress.tasks]) + if self._rich_divert: + self._rich_diverted_reports += progress_reports + '\n' + if self._record_reports: + self._recorded_reports += progress_reports + '\n' + outer_comp = self._execution_stack[0] + # store recorded reports on outer-most Composition if self._rich_divert: - self._rich_diverted_reports += update + '\n' + outer_comp.rich_diverted_reports = self._rich_diverted_reports if self._record_reports: - self._recorded_reports += update + '\n' + outer_comp.recorded_reports = self._recorded_reports - @staticmethod - def node_execution_report(node, + def node_execution_report(self, + node, input_val=None, output_val=None, report_output=ReportOutput.USE_PREFS, + report_params=ReportParams.OFF, context=None): """ Generates formatted output report for the `node ` of a `Composition` or a `Mechanism`. Called by `report_output ` for execution of a Composition, and directly by the `execute ` method of a `Mechanism` when executed on its own. + Allows user to specify *PARAMS* or 'parameters' to induce reporting of all parameters, and a listing of + individual parameters to list just those. + Arguments --------- @@ -867,17 +1019,16 @@ def node_execution_report(node, # Use TERSE format if that has been specified by report_output (i.e., in the arg of an execution method), # or as the reportOutputPref for a node when USE_PREFS is in effect node_pref = convert_to_list(node.reportOutputPref) + # Get reportOutputPref if specified, and remove from node_pref (for param processing below) + report_output_pref = [node_pref.pop(node_pref.index(pref)) + for pref in node_pref if isinstance(pref, ReportOutput)] + node_params_prefs = node_pref if (report_output is ReportOutput.TERSE - or (ReportOutput.TERSE in node_pref - and report_output is not ReportOutput.FULL)): + or (report_output is not ReportOutput.FULL and ReportOutput.TERSE in report_output_pref)): return f'[{node_panel_color}] {node.name} executed' - - from psyneulink.core.components.shellclasses import Function - - node_report = '' - # Render input -------------------------------------------------------------------------------------------- + if input_val is None: input_val = node.get_input_values(context) # FIX: kmantel: previous version would fail on anything but iterables of things that can be cast to floats @@ -887,40 +1038,181 @@ def node_execution_report(node, except TypeError: input_string = input_val - node_report += f"input: {input_string}" + input_report = f"input: {input_string}" # Render output -------------------------------------------------------------------------------------------- + if output_val is None: output = node.output_port.parameters.value._get(context) # FIX: kmantel: previous version would fail on anything but iterables of things that can be cast to floats # if you want more specific output, you can add conditional tests here try: + # output_string = re.sub(r'[\[,\],\n]', '', str([float("{:0.3}".format(float(i))) for i in output_val])) output_string = re.sub(r'[\[,\],\n]', '', str([float("{:0.3}".format(float(i))) for i in output_val])) except TypeError: output_string = output - node_report += f"\noutput: {output_string}" + output_report = f"output: {output_string}" # Render params if specified ------------------------------------------------------------------------------- + + from psyneulink.core.components.shellclasses import Function + report_params = convert_to_list(report_params) params = {p.name: p._get(context) for p in node.parameters} try: - # FIX 3/11/21 ALLOW SPECIFYING INDIVIDUAL PARAMS BY NAME IN LIST - include_params = any(re.match('param(eter)?s?', pref, flags=re.IGNORECASE) for pref in node_pref) - except TypeError: - include_params = False + # Check for PARAMS keyword (or 'parameters') and remove from node_prefs if there + if node_params_prefs and isinstance(node_params_prefs[0],list): + node_params_prefs = node_params_prefs[0] + params_keyword = [node_params_prefs.pop(node_params_prefs.index(p)) + for p in node_params_prefs if re.match('param(eter)?s?', p, flags=re.IGNORECASE)] + # Get any parameters for the node itself + node_params = [node_params_prefs.pop(node_params_prefs.index(p)) + for p in node_params_prefs if p in params] + # If any are left, assume they are for the node's function + function_params = node_params_prefs + # Display parameters if any are specified + include_params = node_params or function_params or params_keyword or report_params + except (TypeError, IndexError): + assert False, f'PROGRAM ERROR: Problem processing reportOutputPref args for {node.name}.' + # include_params = False + + params_string = '' + function_params_string = '' if include_params: - # print("- params:") - params_string = (f"\n- params:") + + def param_is_specified(name, specified_set): + """Helper method: check whether param has been specified based on options""" + + from psyneulink.core.components.mechanisms.mechanism import Mechanism + from psyneulink.core.components.mechanisms.processing.objectivemechanism import ObjectiveMechanism + from psyneulink.core.components.mechanisms.processing.compositioninterfacemechanism \ + import CompositionInterfaceMechanism + from psyneulink.core.components.mechanisms.modulatory.modulatorymechanism \ + import ModulatoryMechanism_Base + + # Helper methods for testing whether param satisfies specifications ----------------------------- + + def get_controller(proj): + """Helper method: get modulator (controller) of modulated params""" + # if not isinstance(proj.sender.owner, CompositionInterfaceMechanism): + if isinstance(proj.sender.owner, ModulatoryMechanism_Base): + return proj.sender.owner.name + # mediating a projection from a ModulatoryMechanism in a Composition in which this is nested + if not isinstance(proj.sender.owner, CompositionInterfaceMechanism): + assert False, f'PROGRAM ERROR Projection to ParameterPort for {param_name} of {node.name} ' \ + f'from non ModulatoryMechanism' + # Recursively call to get ModulatoryMechanism in outer Composition + return get_controller(proj.sender.owner.afferents[0]) + + def is_modulated(): + """Helper method: determine whether parameter is being modulated + by checking whether ParameterPort receives aControlProjection + """ + try: + if isinstance(node, Mechanism): + if name in node.parameter_ports.names: + param_port = node.parameter_ports[name] + if param_port.mod_afferents: + controller_names = [get_controller(c) for c in param_port.mod_afferents] + controllers_str = ' and '.join(controller_names) + return f'modulated by {controllers_str}' + except: + print(f'Failed to find {name} on {node.name}') + # return '' + + + def get_monitor(proj): + """Helper method: get modulator (controller) of modulated params""" + # if not isinstance(proj.sender.owner, CompositionInterfaceMechanism): + if isinstance(proj.receiver.owner, (ObjectiveMechanism, ModulatoryMechanism_Base)): + return proj.receiver.owner.name + # Mediating a projection from a monitored Mechanism to a Composition in which it is nested, so + # recursively call to get receiver in outer Composition + if isinstance(proj.receiver.owner, CompositionInterfaceMechanism) and proj.receiver.owner.efferents: + # owners = [] + # for efferent in proj.receiver.owner.efferents: + # owner = get_monitor(efferent) + # if owner: + # owners.extend(owner) + # return(owners) + owners = [get_monitor(efferent) for efferent in proj.receiver.owner.efferents] + return owners + + + def is_monitored(): + """Helper method: determine whether parameter is being monitored by checking whether OutputPort + sends a MappingProjection to an ObjectiveMechanism or ControlMechanism. + """ + try: + if name is VALUE and isinstance(node, Mechanism): + monitor_names = [] + for output_port in node.output_ports: + monitors = [] + for proj in output_port.efferents: + monitor = get_monitor(proj) + if isinstance(monitor, list): + monitors.extend(monitor) + else: + monitors.append(monitor) + monitor_names.extend([monitor_name for monitor_name in monitors if monitor_name]) + if monitor_names: + monitor_str = ' and '.join(monitor_names) + return f'monitored by {monitor_str}' + except: + print(f'Failed to find {name} on {node.name}') + # return '' + + def is_logged(): + pass + + # Evaluate tests: ----------------------------------------------------------------------- + + # Get modulated and monitored descriptions if they apply + mod_str = is_modulated() + monitor_str = is_monitored() + if monitor_str and mod_str: + control_str = " and ".join([monitor_str, mod_str]) + else: + control_str = monitor_str or mod_str + if control_str: + control_str = f' ({control_str})' + + # Include if param is explicitly specified or ReportParams.ALL (or 'params') is specified + if (name in specified_set + # FIX: ADD SUPPORT FOR ReportParams.ALL + # PARAMS specified as keyword to display all params + or include_params is params_keyword): + return control_str or True + + # Include if param is modulated and ReportParams.MODULATED (CONTROLLED) is specified + if any(k in report_params for k in (ReportParams.MODULATED, ReportParams.CONTROLLED)) and mod_str: + return control_str + + # FIX: NEED TO FILTER OUT RESPONSES TO FUNCTION VALUE AND TO OBJECTIVE MECHANISM ISELF + # # Include if param is monitored and ReportParams.MONITORED is specified + # if ReportParams.MONITORED in report_params and monitor_str: + # return control_str + + # Include if param is being logged and ReportParams.LOGGED is specified + if report_params is ReportParams.LOGGED: + pass + + return False + + # Test whether param matches specifications: ----------------------------------------- + # Sort for consistency of output params_keys_sorted = sorted(params.keys()) for param_name in params_keys_sorted: + + # Check for function + param_is_function = False # No need to report: # function_params here, as they will be reported for the function itself below; # input_ports or output_ports, as these are inherent in the structure if param_name in {FUNCTION_PARAMS, INPUT_PORTS, OUTPUT_PORTS}: continue - param_is_function = False param_value = params[param_name] if isinstance(param_value, Function): param = param_value.name @@ -931,30 +1223,67 @@ def node_execution_report(node, elif isinstance(param_value, (types.FunctionType, types.MethodType)): param = param_value.__node__.__class__.__name__ param_is_function = True - else: - param = param_value - params_string += f"\n\t{param_name}: {str(param).__str__().strip('[]')}" + + # Node param(s) + elif param_is_specified(param_name, node_params): + # Put in params_string if param is specified or 'params' is specified + param_value = params[param_name] + if not params_string: + # Add header + params_string = (f"params:") + params_string += f"\n\t{param_name}: {str(param_value).__str__().strip('[]')}" + if node_params: + node_params.pop(node_params.index(param_name)) + # Don't include functions in params_string yet (to keep at bottom of report) + continue + + # Function param(s) if param_is_function: # Sort for consistency of output - func_params_keys_sorted = sorted(node.function.parameters.names()) + # func_params_keys_sorted = sorted(node.function.parameters.names()) + func_params_keys_sorted = sorted(getattr(node, param_name).parameters.names()) + header_printed = False for fct_param_name in func_params_keys_sorted: - params_string += ("\n\t\t{}: {}". - format(fct_param_name, - str(getattr(node.function.parameters, - fct_param_name)._get(context) - ).__str__().strip("[]") - ) - ) + # Put in function_params_string if function param is specified or 'params' is specified + # (appended to params_string below to keep functions at bottom of report) + modulated = False + qualification = param_is_specified(fct_param_name, function_params) + if qualification: + if not header_printed: + function_params_string += f"\n\t{param_name}: {param_value.name.__str__().strip('[]')}" + header_printed = True + param_value = getattr(getattr(node,param_name).parameters,fct_param_name)._get(context) + param_value = np.squeeze(param_value) + param_value_str = str(param_value).__str__().strip('[]') + if not params_string: + params_string = (f"params:") + if isinstance(qualification, str): + qualification = qualification + else: + qualification = '' + function_params_string += f"\n\t\t{fct_param_name}: {param_value_str}{qualification}" + if function_params: + function_params.pop(function_params.index(fct_param_name)) + + assert not node_params, f"PROGRAM ERROR in execution of Report.node_execution_report() " \ + f"for '{node.name}': {node_params} remaining in node_params." + if function_params: + raise ReportError(f"Unrecognized param(s) specified in " + f"reportOutputPref for '{node.name}': '{', '.join(function_params)}'.") + + params_string += function_params_string + # Generate report ------------------------------------------------------------------------------- - if include_params: + if params_string: width = 100 expand = True - node_report = RenderGroup(node_report,Panel(params_string)) - params_string + node_report = RenderGroup(input_report,Panel(params_string), output_report) else: width = None expand = False + node_report = f'{input_report}\n{output_report}' + return Panel(node_report, box=node_panel_box, border_style=node_panel_color, @@ -962,3 +1291,17 @@ def node_execution_report(node, expand=expand, title=f'[{node_panel_color}]{node.name}', highlight=True) + + @property + def _execution_stack_depth(self): + return len(self._execution_stack) - 1 + + @property + def _nested(self): + from psyneulink.core.compositions.composition import Composition + return any(isinstance(c, Composition) for c in self._execution_stack) + + @property + def _simulating(self): + from psyneulink.core.components.mechanisms.mechanism import Mechanism + return any(isinstance(c, Mechanism) for c in self._execution_stack) diff --git a/psyneulink/core/globals/keywords.py b/psyneulink/core/globals/keywords.py index ebd358962c9..ea90b8cf863 100644 --- a/psyneulink/core/globals/keywords.py +++ b/psyneulink/core/globals/keywords.py @@ -91,8 +91,8 @@ 'OUTPUT_PORT', 'OUTPUT_PORT_PARAMS', 'output_port_spec_to_parameter_name', 'OUTPUT_PORTS', 'OUTPUT_TYPE', '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', 'PARAMS', - 'PARAMS_DICT', 'PATHWAY', 'PATHWAY_PROJECTION', 'PEARSON', + 'PARAMETER', 'PARAMETER_CIM_NAME', 'PARAMETER_PORT', 'PARAMETER_PORT_PARAMS', 'PARAMETER_PORTS', + 'PARAMETERS', 'PARAMS', 'PARAMS_DICT', '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', @@ -419,6 +419,7 @@ def _is_metric(metric): PREVIOUS_VALUE = 'previous_value' LABELS = 'labels' PARAMS = "params" +PARAMETERS = 'params' NAME = "name" PREFS_ARG = "prefs" CONTEXT = "context" diff --git a/psyneulink/core/globals/parameters.py b/psyneulink/core/globals/parameters.py index 4ccd62ab3f1..f72e69c983f 100644 --- a/psyneulink/core/globals/parameters.py +++ b/psyneulink/core/globals/parameters.py @@ -78,7 +78,7 @@ ` method, see `BasicsAndPrimer_Parameters`. -.. _technical_note:: +.. technical_note:: Developers must keep in mind state when writing new Components for PsyNeuLink. Any parameters or values that may change during a `run ` must become stateful Parameters, or they are at risk of computational diff --git a/psyneulink/core/globals/preferences/preferenceset.py b/psyneulink/core/globals/preferences/preferenceset.py index d01b16b5582..9eb1be973e6 100644 --- a/psyneulink/core/globals/preferences/preferenceset.py +++ b/psyneulink/core/globals/preferences/preferenceset.py @@ -15,7 +15,7 @@ by a PreferenceSet, which specifies the Standard Preferences -------------------- +-------------------- The following preferences are available for all Components (see `Component prefs ` for additional details): @@ -27,13 +27,15 @@ .. _PreferenceSet_reportOutputPref: -* **reportOutputPref** (list['params', `ReportOutput`]: default ReportOutput.OFF) - enables/disables - and determines format and content for reporting execution of the `Component` (see `ReportOutput` for options). - If 'params' (or 'parameters') is specified, then the Component's `Parameters are included along with its input and - output when `ReportOutput.FULL` is also specified. If the Component is a `Mechanism` executed within a `Composition`, - this preference may be overridden by the **report_output** argument specified in any of the Composition's - `execution methods ` (see `execution reporting ` - for additional details). +* **reportOutputPref** (`ReportOutput`, *PARAMS*, str, list): default ReportOutput.OFF) - enables/disables and + determines format and content for reporting execution of the `Component` (see `ReportOutput` for options). If + *PARAMS* is specified, then `ReportOutput.FULL` is used automatically (rather than the default, `ReportOutput.OFF`), + and the Component's `Parameters` are included along with its input and output. A list of specific Parameters of + the Component and/or its `function ` can also be specified, in which case only those are + included. If the Component is a `Mechanism` executed within a `Composition`, its preference is overridden + by the **report_output** argument specified in any of the Composition's `execution methods + ` unless ReportOutput.USE_PREFS is specified in the argument; see below for + examples and `execution reporting ` for additional details. * **logPref** (`LogCondition` : default LogCondition.OFF) - sets `LogCondition` for a given Component; @@ -43,9 +45,158 @@ used for modulating parameters by runtime specification (in pathway); COMMENT -.. _technical_note:: +.. technical_note:: * **deliverPref** (LogCondition, default: LogCondition.OFF) - sets whether attribute data are added to context rpc pipeline for delivery to external applications. + + +Examples +-------- + +reportOutputPref +~~~~~~~~~~~~~~~~ + +By default, executing a Component, such as the Mechanism below, does not produce any output: + + >>> import psyneulink as pnl + >>> my_mech = pnl.TransferMechanism() + >>> my_mech.execute() + + +Output can be specified by specifying a value of `ReportOutput` as the Mechanism's `reportOutputPref` preference. +Assigning `ReportOutput.FULL` generates a report of its input and output values when it was executed: + + >>> my_mech.reportOutputPref = pnl.ReportOutput.FULL + >>> my_mech.execute() + ╭─ My Mechanism ─╮ + │ input: 0.0 │ + │ output: 0.0 │ + ╰────────────────╯ + +Assigning `ReportOutput.TERSE` generates a simpler report: + + >>> my_mech.reportOutputPref = pnl.ReportOutput.TERSE + >>> my_mech.execute() + My Mechanism executed + +This can be useful when there are many Components executed (e.g., as part of the `execution ` +of a complex `Composition`. + +Assigning the *PARAMS* keyword produces a display of the Mechanism's input and output as well as the value of +all of its `Parameters` (for brevity, not all are shown below): + + >>> my_mech.reportOutputPref = pnl.PARAMS + >>> my_mech.execute() + ╭────────────────────────────────────────── My Mechanism ──────────────────────────────────────────╮ + │ input: 0.0 │ + │ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │ + │ │ params: │ │ + ... + │ │ integration_rate: 0.5 │ │ + ... + │ │ noise: 0.0 │ │ + ... + │ │ function: Linear Function-6 │ │ + │ │ intercept: 0.0 │ │ + ... + │ │ slope: 1.0 │ │ + │ │ value: 0.0 │ │ + │ │ variable: 0.0 │ │ + │ │ integrator_function: AdaptiveIntegrator Function-1 │ │ + ... + │ │ previous_value: 0 │ │ + │ │ rate: 0.5 │ │ + │ │ value: 0.0 │ │ + │ │ variable: 0 │ │ + │ │ termination_measure: Distance Function-5 │ │ + ... + │ │ metric: max_abs_diff │ │ + │ │ normalize: False │ │ + │ │ value: 0.0 │ │ + │ │ variable: 0 0 │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ output: 0.0 │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ + +Note that specifying *PARAMS* forces a full output (i.e., equivalent to also specifying `ReportOutput.FULL`). + +Generally, not all of a Component's `Parameters` are of interest. The display can be restricted to +just those of interest by including them in a list specified for reportOutputPref: + + >>> my_mech.reportOutputPref = ['integration_rate', 'slope', 'rate'] + >>> my_mech.execute() + ╭────────────────────────────────────────── My Mechanism ──────────────────────────────────────────╮ + │ input: 0.0 │ + │ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │ + │ │ params: │ │ + │ │ integration_rate: 0.5 │ │ + │ │ function: Linear Function-6 │ │ + │ │ slope: 1.0 │ │ + │ │ integrator_function: AdaptiveIntegrator Function-1 │ │ + │ │ rate: 0.5 │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ output: 0.0 │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ + + +This can be overridden by specifying `ReportOutput.TERSE` (i.e., without having to delete all of the parameter +specifications, which can be useful when testing): + + >>> my_mech.reportOutputPref = ['integration_rate', 'slope', 'rate', pnl.ReportOutput.TERSE] + >>> my_mech.execute() + My Mechanism executed + +When a Mechanism is executed as part of a Composition, the Composition's reportOutputPref takes precedence: + + >>> my_comp = pnl.Composition(pathways=[my_mech]) + >>> my_mech.reportOutputPref = ['integration_rate', 'slope', 'rate'] + >>> my_comp.run() + + +Note that the Composition's setting, which is ReportOutput.OFF by default, overrode the Mechanism's +reportOutputPref settings. The **report_output** argument of a Composition's `execution method +` can also be used to specify report contents; this too overrides +the Mechanism's reportOutputPref setting: + + >>> my_mech.reportOutputPref = ['integration_rate', 'slope', 'rate', pnl.ReportOutput.FULL] + >>> my_comp.run(report_output=pnl.ReportOutput.TERSE) + Composition-0 TRIAL 0 ==================== + Time Step 0 --------- + My Mechanism executed + +Note that the report for the execution of a Composition contains information about the `TRIAL ` +and `TIME_STEP ` in which the Mechanism executed. + +A more complete report of the execution can be generated using the `Report.FULL` and `Report.USE_PREFS` options in the +**report_output** argument of a Composition's `execution methods `, that also includes +the input and output for the Composition: + + >>> my_comp = pnl.Composition(pathways=[my_mech]) + >>> my_mech.reportOutputPref = ['integration_rate', 'slope', 'rate'] + >>> my_comp.run(report_output=pnl.ReportOutput.FULL) + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Composition-0: Trial 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ ┃ + ┃ input: [[0.0]] ┃ + ┃ ┃ + ┃ ┌──────────────────────────────────────────── Time Step 0 ────────────────────────────────────────────┐ ┃ + ┃ │ ╭────────────────────────────────────────── My Mechanism ──────────────────────────────────────────╮ │ ┃ + ┃ │ │ input: 0.0 │ │ ┃ + ┃ │ │ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │ │ ┃ + ┃ │ │ │ params: │ │ │ ┃ + ┃ │ │ │ integration_rate: 0.5 │ │ │ ┃ + ┃ │ │ │ function: Linear Function-6 │ │ │ ┃ + ┃ │ │ │ slope: 1.0 │ │ │ ┃ + ┃ │ │ │ integrator_function: AdaptiveIntegrator Function-1 │ │ │ ┃ + ┃ │ │ │ rate: 0.5 │ │ │ ┃ + ┃ │ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ │ ┃ + ┃ │ │ output: 0.0 │ │ ┃ + ┃ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ │ ┃ + ┃ └──────────────────────────────────────────────────────────────────────────────────────────────────────┘ ┃ + ┃ ┃ + ┃ result: [[0.0]] ┃ + ┃ ┃ + ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + """ import abc diff --git a/psyneulink/core/llvm/execution.py b/psyneulink/core/llvm/execution.py index df3980be342..8c3ba58443f 100644 --- a/psyneulink/core/llvm/execution.py +++ b/psyneulink/core/llvm/execution.py @@ -460,12 +460,13 @@ def _get_input_struct(self, inputs): def freeze_values(self): self.__frozen_vals = copy.deepcopy(self._data_struct) - def execute_node(self, node, inputs=None): + def execute_node(self, node, inputs=None, context=None): # We need to reconstruct the input dictionary here if it was not provided. # This happens during node execution of nested compositions. assert len(self._execution_contexts) == 1 if inputs is None and node is self._composition.input_CIM: - context = self._execution_contexts[0] + if context is None: + context = self._execution_contexts[0] port_inputs = {origin_port:[proj.parameters.value._get(context) for proj in p[0].path_afferents] for (origin_port, p) in self._composition.input_CIM_ports.items()} inputs = {} for p, v in port_inputs.items(): @@ -493,6 +494,8 @@ def execute_node(self, node, inputs=None): print("RAN: {}. Params: {}".format(node, self.extract_node_params(node))) print("RAN: {}. Results: {}".format(node, self.extract_node_output(node))) + node._propagate_most_recent_context(context) + @property def _bin_exec_func(self): if self.__bin_exec_func is None: diff --git a/psyneulink/library/compositions/autodiffcomposition.py b/psyneulink/library/compositions/autodiffcomposition.py index efdfbac4b35..555ee97a083 100644 --- a/psyneulink/library/compositions/autodiffcomposition.py +++ b/psyneulink/library/compositions/autodiffcomposition.py @@ -144,6 +144,8 @@ from psyneulink.library.components.mechanisms.processing.objective.comparatormechanism import ComparatorMechanism from psyneulink.core.compositions.composition import Composition, NodeRole from psyneulink.core.compositions.composition import CompositionError +from psyneulink.core.compositions.report \ + import ReportOutput, ReportParams, ReportProgress, ReportSimulations, ReportDevices from psyneulink.core.globals.context import Context, ContextFlags, handle_external_context from psyneulink.core.globals.keywords import SOFT_CLAMP from psyneulink.core.scheduling.scheduler import Scheduler @@ -476,12 +478,13 @@ def execute(self, runtime_params=None, execution_mode:pnlvm.ExecutionMode = pnlvm.ExecutionMode.Python, skip_initialization=False, - report_output=False, - report_progress=False, - report_simulations=False, - report_to_devices=None, + report_output:ReportOutput=ReportOutput.OFF, + report_params:ReportOutput=ReportParams.OFF, + report_progress:ReportProgress=ReportProgress.OFF, + report_simulations:ReportSimulations=ReportSimulations.OFF, + report_to_devices:ReportDevices=None, report=None, - progress_report=None, + run_report=None, ): self._assign_execution_ids(context) context.composition = self @@ -517,9 +520,9 @@ def execute(self, scheduler.get_clock(context)._increment_time(TimeScale.TRIAL) # MODIFIED 3/2/21 NEW: FIX: CAUSES CRASH... NEEDS TO BE FIXED - # progress.report_output(self, progress_report, scheduler, show_output, 'trial', context) + # progress.report_output(self, run_report, scheduler, show_output, 'trial', context) # MODIFIED 3/2/21 END - report.report_progress(self, progress_report, context) + report.report_progress(self, run_report, context) return output diff --git a/requirements.txt b/requirements.txt index 419e1015923..52876189f86 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ elfi<0.7.8 graphviz<0.17.0 grpcio<1.35.0 grpcio-tools<1.35.0 -llvmlite<0.36 +llvmlite<0.37 matplotlib<3.3.4 networkx<2.6 numpy<1.20.2 diff --git a/tests/composition/test_report.py b/tests/composition/test_report.py index 91e2eda40f2..db0b4dae517 100644 --- a/tests/composition/test_report.py +++ b/tests/composition/test_report.py @@ -5,7 +5,7 @@ import pytest import psyneulink as pnl -from psyneulink.core.compositions.report import ReportOutput, ReportProgress, ReportDevices +from psyneulink.core.compositions.report import ReportOutput, ReportProgress, ReportSimulations, ReportDevices class TestReport(): @@ -70,7 +70,7 @@ def test_simple_output_and_progress(self): report_progress=ReportProgress.ON, report_to_devices=ReportDevices.DIVERT) actual_output = comp.rich_diverted_reports - expected_output = '\'\\nCOMP TRIAL 1 ====================\\n Time Step 0 ---------\\n a executed\\n Time Step 1 ---------\\n b executed\\n Time Step 2 ---------\\n c executed\\n[red]Executing COMP...\\n\'' + expected_output = '\'\\nCOMP TRIAL 1 ====================\\n Time Step 0 ---------\\n a executed\\n Time Step 1 ---------\\n b executed\\n Time Step 2 ---------\\n c executed\\nCOMP: Executed 1 of 1 trials\\n\'' assert repr(actual_output) == expected_output comp.execute(report_output=ReportOutput.USE_PREFS, report_to_devices=ReportDevices.DIVERT) @@ -82,7 +82,7 @@ def test_simple_output_and_progress(self): report_progress=ReportProgress.ON, report_to_devices=ReportDevices.DIVERT) actual_output = comp.rich_diverted_reports - expected_output = '\nCOMP TRIAL 3 ====================\n Time Step 0 ---------\n╭───── a ─────╮\n│ input: 0.0 │\n│ output: 0.0 │\n╰─────────────╯\n Time Step 1 ---------\n Time Step 2 ---------\n╭───── c ─────╮\n│ input: 0.0 │\n│ output: 0.0 │\n╰─────────────╯\n[red]Executing COMP...\n' + expected_output = '\nCOMP TRIAL 3 ====================\n Time Step 0 ---------\n╭───── a ─────╮\n│ input: 0.0 │\n│ output: 0.0 │\n╰─────────────╯\n Time Step 1 ---------\n Time Step 2 ---------\n╭───── c ─────╮\n│ input: 0.0 │\n│ output: 0.0 │\n╰─────────────╯\nCOMP: Executed 1 of 1 trials\n' assert actual_output == expected_output comp.execute(report_output=ReportOutput.FULL, report_to_devices=ReportDevices.DIVERT) @@ -94,7 +94,7 @@ def test_simple_output_and_progress(self): report_progress=ReportProgress.ON, report_to_devices=ReportDevices.DIVERT) actual_output = comp.rich_diverted_reports - expected_output = '\n┏━━ COMP: Trial 5 ━━┓\n┃ ┃\n┃ input: [[0.0]] ┃\n┃ ┃\n┃ ┌─ Time Step 0 ──┐ ┃\n┃ │ ╭───── a ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ ┌─ Time Step 1 ──┐ ┃\n┃ │ ╭───── b ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ ┌─ Time Step 2 ──┐ ┃\n┃ │ ╭───── c ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ result: [[0.0]] ┃\n┃ ┃\n┗━━━━━━━━━━━━━━━━━━━━━┛\n\n[red]Executing COMP...\n' + expected_output = '\n┏━━ COMP: Trial 5 ━━┓\n┃ ┃\n┃ input: [[0.0]] ┃\n┃ ┃\n┃ ┌─ Time Step 0 ──┐ ┃\n┃ │ ╭───── a ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ ┌─ Time Step 1 ──┐ ┃\n┃ │ ╭───── b ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ ┌─ Time Step 2 ──┐ ┃\n┃ │ ╭───── c ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ result: [[0.0]] ┃\n┃ ┃\n┗━━━━━━━━━━━━━━━━━━━━━┛\n\nCOMP: Executed 1 of 1 trials\n' assert actual_output == expected_output # def test_two_mechs_in_a_time_step(self): @@ -130,100 +130,183 @@ def test_simple_output_and_progress(self): # expected_output = '\n┏━━━ COMP: Trial 0 ━━━┓\n┃ ┃\n┃ input: [[0.0], [0.0]] ┃\n┃ ┃\n┃ ┌─ Time Step 0 ──┐ ┃\n┃ │ ╭───── b ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ │ ╭───── a ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ ┌─ Time Step 1 ──┐ ┃\n┃ │ ╭───── c ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ result: [[0.0]] ┃\n┃ ┃\n┗━━━━━━━━━━━━━━━━━━━━━━━┛\n\nCOMP: Executed 1 of 2 trials\n┏━━━ COMP: Trial 0 ━━━┓\n┃ ┃\n┃ input: [[0.0], [0.0]] ┃\n┃ ┃\n┃ ┌─ Time Step 0 ──┐ ┃\n┃ │ ╭───── b ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ │ ╭───── a ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ ┌─ Time Step 1 ──┐ ┃\n┃ │ ╭───── c ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ result: [[0.0]] ┃\n┃ ┃\n┗━━━━━━━━━━━━━━━━━━━━━━━┛\n\n┏━━━ COMP: Trial 1 ━━━┓\n┃ ┃\n┃ input: [[0.0], [0.0]] ┃\n┃ ┃\n┃ ┌─ Time Step 0 ──┐ ┃\n┃ │ ╭───── b ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ │ ╭───── a ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ ┌─ Time Step 1 ──┐ ┃\n┃ │ ╭───── c ─────╮ │ ┃\n┃ │ │ input: 0.0 │ │ ┃\n┃ │ │ output: 0.0 │ │ ┃\n┃ │ ╰─────────────╯ │ ┃\n┃ └─────────────────┘ ┃\n┃ ┃\n┃ result: [[0.0]] ┃\n┃ ┃\n┗━━━━━━━━━━━━━━━━━━━━━━━┛\n\nCOMP: Executed 2 of 2 trials' # assert actual_output == expected_output - # def test_nested_comps_output(self): - # """Test of nested Compositions with simulations executed by OCMs""" - # - # with_inner_controller = True - # with_outer_controller = True - # - # # instantiate mechanisms and inner comp - # ia = pnl.TransferMechanism(name='ia') - # ib = pnl.TransferMechanism(name='ib') - # icomp = pnl.Composition(name='icomp', controller_mode=pnl.BEFORE) - # - # # set up structure of inner comp - # icomp.add_node(ia, required_roles=pnl.NodeRole.INPUT) - # icomp.add_node(ib, required_roles=pnl.NodeRole.OUTPUT) - # icomp.add_projection(pnl.MappingProjection(), sender=ia, receiver=ib) - # - # # add controller to inner comp - # if with_inner_controller: - # icomp.add_controller( - # pnl.OptimizationControlMechanism( - # agent_rep=icomp, - # features=[ia.input_port], - # name="iController", - # objective_mechanism=pnl.ObjectiveMechanism( - # monitor=ib.output_port, - # function=pnl.SimpleIntegrator, - # name="iController Objective Mechanism" - # ), - # function=pnl.GridSearch(direction=pnl.MAXIMIZE), - # control_signals=[pnl.ControlSignal(projections=[(pnl.SLOPE, ia)], - # variable=1.0, - # intensity_cost_function=pnl.Linear(slope=0.0), - # allocation_samples=pnl.SampleSpec(start=1.0, - # stop=10.0, - # num=4))]) - # ) - # - # # instantiate outer comp - # ocomp = pnl.Composition(name='ocomp', controller_mode=pnl.BEFORE) - # - # # setup structure of outer comp - # ocomp.add_node(icomp) - # - # ocomp._analyze_graph() - # - # # add controller to outer comp - # if with_outer_controller: - # ocomp.add_controller( - # pnl.OptimizationControlMechanism( - # agent_rep=ocomp, - # # features=[ia.input_port], - # features=[ocomp.input_CIM.output_ports[0]], - # name="oController", - # objective_mechanism=pnl.ObjectiveMechanism( - # monitor=ib.output_port, - # function=pnl.SimpleIntegrator, - # name="oController Objective Mechanism" - # ), - # function=pnl.GridSearch(direction=pnl.MAXIMIZE), - # control_signals=[pnl.ControlSignal(projections=[(pnl.SLOPE, ia)], - # variable=1.0, - # intensity_cost_function=pnl.Linear(slope=0.0), - # allocation_samples=pnl.SampleSpec(start=1.0, - # stop=10.0, - # num=3))]) - # ) - # - # inputs_dict = { - # icomp: - # { - # ia: [[-2], [1]] - # } - # } - # - # def inputs_generator_function(): - # for i in range(2): - # yield { - # icomp: - # { - # ia: inputs_dict[icomp][ia][i] - # } - # } - # - # inputs_generator_instance = inputs_generator_function() - # - # # ocomp.run(inputs=inputs_generator_function) - # # ocomp.run(inputs=inputs_generator_instance, report_progress=['simulations']) - # # ocomp.run(inputs=inputs_dict, report_progress=True) - # # ocomp.run(inputs=inputs_dict, report_progress=['simulations']) - # - # ocomp.run(inputs={icomp:-2}, report_output=FULL, report_simulations=True, report_to_devices=ReportDevices.DIVERT) - # actual_output = ocomp.rich_diverted_reports - # expected_output = '\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n Time Step 0 ---------\n Time Step 0 ---------\n Time Step 1 ---------\nocomp: Executed 1 of 1 trials\nocomp: Simulated 3 trials\nicomp: Executed 1 of 1 trials\nicomp: Simulated 4 trials\nicomp: Executed 1 of 1 trials\nicomp: Simulated 4 trials\nicomp: Executed 1 of 1 trials\nicomp: Simulated 4 trials\nicomp: Executed 1 of 1 trials\nicomp: Simulated 4 trials' - # assert actual_output == expected_output + @pytest.mark.skipif(sys.platform == 'win32', reason="") + def test_nested_comps_output(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. + """ + + # instantiate mechanisms and inner comp + ia = pnl.TransferMechanism(name='ia') + ib = pnl.TransferMechanism(name='ib') + icomp = pnl.Composition(name='icomp', controller_mode=pnl.AFTER) + + # set up structure of inner comp + icomp.add_node(ia, required_roles=pnl.NodeRole.INPUT) + icomp.add_node(ib, required_roles=pnl.NodeRole.OUTPUT) + icomp.add_projection(pnl.MappingProjection(), sender=ia, receiver=ib) + + # add controller to inner comp + icomp.add_controller( + pnl.OptimizationControlMechanism( + agent_rep=icomp, + features=[ia.input_port], + name="iController", + objective_mechanism=pnl.ObjectiveMechanism( + monitor=ib.output_port, + function=pnl.SimpleIntegrator, + name="iController Objective Mechanism" + ), + function=pnl.GridSearch(direction=pnl.MAXIMIZE), + control_signals=[pnl.ControlSignal(projections=[(pnl.SLOPE, ia)], + variable=1.0, + intensity_cost_function=pnl.Linear(slope=0.0), + allocation_samples=pnl.SampleSpec(start=1.0, + stop=10.0, + num=3))]) + ) + + # instantiate outer comp + ocomp = pnl.Composition(name='ocomp', controller_mode=pnl.BEFORE) + + # setup structure of outer comp + ocomp.add_node(icomp) + + ocomp._analyze_graph() + + # add controller to outer comp + ocomp.add_controller( + pnl.OptimizationControlMechanism( + agent_rep=ocomp, + # features=[ia.input_port], + features=[ocomp.input_CIM.output_ports[0]], + name="oController", + objective_mechanism=pnl.ObjectiveMechanism( + monitor=ib.output_port, + function=pnl.SimpleIntegrator, + name="oController Objective Mechanism" + ), + function=pnl.GridSearch(direction=pnl.MAXIMIZE), + control_signals=[pnl.ControlSignal(projections=[(pnl.SLOPE, ia)], + variable=1.0, + intensity_cost_function=pnl.Linear(slope=0.0), + allocation_samples=pnl.SampleSpec(start=1.0, + stop=10.0, + num=2))]) + ) + + # set up input using three different formats: + # 1) generator function + # 2) instance of generator function + # 3) inputs dict + inputs_dict = { + icomp: + { + ia: [[-2], [1]] + } + } + + def inputs_generator_function(): + for i in range(2): + yield { + icomp: + { + ia: inputs_dict[icomp][ia][i] + } + } + + inputs_generator_instance = inputs_generator_function() + + ocomp.run(inputs={icomp:-2}, + report_output=ReportOutput.ON, + report_progress=ReportProgress.OFF, + report_simulations=ReportSimulations.OFF, + report_to_devices=ReportDevices.DIVERT + ) + actual_output = ocomp.rich_diverted_reports + expected_output = '\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\n' + assert actual_output == expected_output + + ocomp.run(inputs={icomp:-2}, + report_output=ReportOutput.OFF, + report_progress=ReportProgress.ON, + report_simulations=ReportSimulations.OFF, + report_to_devices=ReportDevices.DIVERT + ) + actual_output = ocomp.rich_diverted_reports + expected_output = 'ocomp: Executed 1 of 1 trials\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Executed 1 of 1 trials (depth: 1)\n' + assert actual_output == expected_output + + ocomp.run(inputs={icomp:-2}, + report_output=ReportOutput.ON, + report_progress=ReportProgress.ON, + report_simulations=ReportSimulations.OFF, + report_to_devices=ReportDevices.DIVERT + ) + actual_output = ocomp.rich_diverted_reports + expected_output = '\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp: Executed 1 of 1 trials\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Executed 1 of 1 trials (depth: 1)\n' + assert actual_output == expected_output + + ocomp.run(inputs={icomp:-2}, + report_output=ReportOutput.ON, + report_progress=ReportProgress.OFF, + report_simulations=ReportSimulations.ON, + report_to_devices=ReportDevices.DIVERT + ) + actual_output = ocomp.rich_diverted_reports + expected_output = '\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\n' + assert actual_output == expected_output + + ocomp.run(inputs={icomp:-2}, + report_output=ReportOutput.OFF, + report_progress=ReportProgress.ON, + report_simulations=ReportSimulations.ON, + report_to_devices=ReportDevices.DIVERT + ) + actual_output = ocomp.rich_diverted_reports + expected_output = 'ocomp: Executed 1 of 1 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n' + assert actual_output == expected_output + + # ocomp.run(inputs={icomp:-2}, + # report_output=ReportOutput.ON, + # report_progress=ReportProgress.ON, + # report_simulations=ReportSimulations.ON, + # report_to_devices=ReportDevices.DIVERT + # ) + # actual_output = ocomp.rich_diverted_reports + # expected_output = '\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp: Executed 1 of 1 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n' + # assert actual_output == expected_output + # + ocomp.run(inputs=inputs_dict, + report_output=ReportOutput.ON, + report_progress=ReportProgress.ON, + report_simulations=ReportSimulations.ON, + report_to_devices=ReportDevices.DIVERT + ) + actual_output = ocomp.rich_diverted_reports + expected_output = '\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp: Executed 1 of 2 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp: Executed 2 of 2 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n' + assert actual_output == expected_output + + ocomp.run(inputs=inputs_generator_instance, + report_output=ReportOutput.ON, + report_progress=ReportProgress.ON, + report_simulations=ReportSimulations.ON, + report_to_devices=ReportDevices.DIVERT + ) + actual_output = ocomp.rich_diverted_reports + expected_output = '\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp: Executed 1 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp: Executed 2 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n' + assert actual_output == expected_output + + ocomp.run(inputs=inputs_generator_function(), + report_output=ReportOutput.ON, + report_progress=ReportProgress.ON, + report_simulations=ReportSimulations.ON, + report_to_devices=ReportDevices.DIVERT + ) + actual_output = ocomp.rich_diverted_reports + expected_output = '\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp: Executed 1 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 0 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp SIMULATION TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 0 ---------\n oController Objective Mechanism executed\nocomp TRIAL 1 ====================\n Time Step 0 ---------\nicomp TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\nicomp SIMULATION TRIAL 0 ====================\n Time Step 0 ---------\n ia executed\n Time Step 0 ---------\n ib executed\n Time Step 0 ---------\n iController Objective Mechanism executed\n icomp executed\n Time Step 1 ---------\n oController Objective Mechanism executed\nocomp: Executed 2 trials\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n ocomp: Simulated 2 trials (depth: 1)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 2)\n icomp: Simulated 3 trials (depth: 3)\n icomp: Executed 1 of 1 trials (depth: 1)\n icomp: Simulated 3 trials (depth: 2)\n' + assert actual_output == expected_output + @pytest.mark.skipif(sys.platform == 'win32', reason="") def test_reportOutputPref_true(self):