Skip to content

Commit

Permalink
Merge branch 'devel'
Browse files Browse the repository at this point in the history
* devel:
  documentation typos
  documenting reinitialize on mechanism
  continuing to add tests and docs for reinitialize
  fixing bugs that prevented initialize method on system from ever executing, and adding pytests for it now that it works
  revising outdated TransferMechanism docs for reinitialize
  continuing to add tests to highlight differences between calling reinitialize on a mechanisms and on a function
  adding nparray_dictionary to docs and revising nparray to use the same helper methods as nparray_dictionary
  Time: fix bug where RUN counter would not be incremented on new runs
  fixing (temporarily) time_step bug in nparray_dictionary; reorganzing nparray_dictionary into methods that can be reused by nparray
  refactoring log to have a dictionary option and writing tests for it
  beginning to add a dictionary alternative to the options for whats returned by the log
  more calls to _dealias_owner_name required in order to create log.nparray
  fixing bug in log: needed to switch between 'value' and mechanism name in several loops in order to assemble log.nparray
  adding more pytests for reinitialize and fixing reinitialize bugs; beginning to update documentation
  adding pytests for reinitializing at both function and mechanism level; cleaning up mistakes in reinitialize() methods along the way
  adding a reinitialize method on mechanisms, which calls reinitialize on its function or integrator_function, then updates the mechanism's value and output states
  beginning to refactor reinitialize as a method on integrator functions
  • Loading branch information
kmantel committed Feb 10, 2018
2 parents 6949e0d + 7225e0c commit b2e3e85
Show file tree
Hide file tree
Showing 20 changed files with 1,326 additions and 462 deletions.
431 changes: 173 additions & 258 deletions psyneulink/components/functions/function.py

Large diffs are not rendered by default.

69 changes: 61 additions & 8 deletions psyneulink/components/mechanisms/mechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,65 @@ def _add_projection_from_mechanism(self, receiver, state, projection, context=No
from psyneulink.components.projections.projection import _add_projection_from
_add_projection_from(sender=self, state=state, projection_spec=projection, receiver=receiver, context=context)

def reinitialize(self, *args):
"""
If the mechanism's `function <Mechanism.function>` is an `Integrator`, or if the mechanism has and
`integrator_function <TransferMechanism.integrator_function>` (see `TransferMechanism`), this method
effectively begins the function's accumulation over again at the specified value, and updates related
attributes on the mechanism.
If the mechanism's `function <Mechanism_Base.function>` is an `Integrator`:
`reinitialize <Mechanism_Base.reinitialize>` first calls the function's own `reinitialize <Integrator.reinitialize>` method, which
typically sets:
- `previous_value <Integrator.previous_value>`
- `initializer <Integrator.initial_value>`
- `value <Integrator.value>`
to the quantity specified. For specific types of Integrator functions, additional values, such as
initial time, must be specified, and additional attributes are reset. See individual functions for
details.
Then, the mechanism sets its `value <Mechanism_Base.value>` to the quantity specified, and updates its
`output states <Mechanism_Base.output_state>`.
If the mechanism has an `integrator_function <TransferMechanism.integrator_function>`:
`reinitialize <Mechanism_Base.reinitialize>` first calls the `integrator_function's <TransferMechanism.integrator_function>` own
`reinitialize <Integrator.reinitialize>` method, which typically sets:
- `previous_value <Integrator.previous_value>`
- `initializer <Integrator.initial_value>`
- `value <Integrator.value>`
to the quantity specified. For specific types of Integrator functions, additional values, such as
initial time, must be specified, and additional attributes are reset. See individual functions for
details.
Then, the mechanism executes its `function <Mechanism_Base.function>` using the quantity specified as the
function's variable. The mechanism's `value <Mechanism_Base.value>` is set to the output of its function.
Finally, the mechanism updates its `output states <Mechanism_Base.output_state>`.
"""
from psyneulink.components.functions.function import Integrator

# If the primary function of the mechanism is an integrator:
# (1) reinitialize it, (2) update value, (3) update output states
if isinstance(self.function_object, Integrator):
new_value = self.function_object.reinitialize(*args)
self.value = np.atleast_2d(new_value)
self._update_output_states(context="REINITIALIZING")

# If the mechanism has an auxiliary integrator function:
# (1) reinitialize it, (2) run the primary function with the new "previous_value" as input
# (3) update value, (4) update output states
elif hasattr(self, "integrator_function"):
new_input = self.integrator_function.reinitialize(*args)
if hasattr(self, "initial_value"):
self.initial_value = np.atleast_1d(*args)[0]
self.value = self.function(new_input, context="REINITIALIZING")
self._update_output_states(context="REINITIALIZING")

def get_current_mechanism_param(self, param_name):
try:
return self._parameter_states[param_name].value
Expand Down Expand Up @@ -2151,11 +2210,6 @@ def initialize(self, value):
"""Assign an initial value to the Mechanism's `value <Mechanism_Base.value>` attribute and update its
`OutputStates <Mechanism_OutputStates>`.
COMMENT:
Takes a number or 1d array and assigns it to the first item of the Mechanism's
`value <Mechanism_Base.value>` attribute.
COMMENT
Arguments
---------
Expand All @@ -2167,8 +2221,8 @@ def initialize(self, value):
if not iscompatible(value, self.value):
raise MechanismError("Initialization value ({}) is not compatiable with value of {}".
format(value, append_type_to_name(self)))
self.value[0] = value
self._update_output_states()
self.value = np.atleast_1d(value)
self._update_output_states(context="INITIAL_VALUE")

def _execute(self,
variable=None,
Expand Down Expand Up @@ -2320,7 +2374,6 @@ def add_states(self, states, context=ADD_STATES):
from psyneulink.components.states.state import _parse_state_type
from psyneulink.components.states.inputstate import InputState, _instantiate_input_states
from psyneulink.components.states.outputstate import OutputState, _instantiate_output_states

# Put in list to standardize treatment below
if not isinstance(states, list):
states = [states]
Expand Down
56 changes: 46 additions & 10 deletions psyneulink/components/mechanisms/processing/transfermechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,38 @@
<OutputState.value>` of each of its `OutputStates <OutputState>`, and to the 1st item of the Mechanism's
`output_values <TransferMechanism.output_values>` attribute.
In some cases, it may be useful to reset the integration of the mechanism back to the original starting point, or a new
one. This can be done using the `reinitialize <AdaptiveIntegrator.reinitialize>` property on the mechanism's
`integrator_function <TransferMechanism.integrator_function>`. The `reinitialize <AdaptiveIntegrator.reinitialize>`
property sets the `integrator_function's <TransferMechanism.integrator_function>`
`initializer <AdaptiveIntegrator.initializer>`, `previous_value <AdaptiveIntegrator.previous_value>`, and
`value <AdaptiveIntegrator.value>` attributes to a specified value.
.. _Transfer_Reinitialization:
Reinitialization
~~~~~~~~~~~~
In some cases, it may be useful to reset the accumulation of a mechanism back to its original starting point, or a new
starting point. This is done using the `reinitialize <AdaptiveIntegrator.reinitialize>` method on the mechanism's
`integrator_function <TransferMechanism.integrator_function>`, or the mechanisms's own `reinitialize
<TransferMechanism.reinitialize>` method.
The `reinitialize <AdaptiveIntegrator.reinitialize>` method of the `integrator_function
<TransferMechanism.integrator_function>` sets:
- the integrator_function's `initializer <AdaptiveIntegrator.initializer>` attribute
- the integrator_function's `previous_value <AdaptiveIntegrator.previous_value>` attribute
- the integrator_function's `value <AdaptiveIntegrator.value>` attribute
to the specified value.
The `reinitialize <TransferMechanism.reinitialize>` method of the `TransferMechanism` first sets:
- the integrator_function's `initializer <AdaptiveIntegrator.initializer>` attribute
- the integrator_function's `previous_value <AdaptiveIntegrator.previous_value>` attribute
- the integrator_function's `value <AdaptiveIntegrator.value>` attribute
- the TransferMechanism's `initial_value <TransferMechanism.initial_value>` attribute
to the specified value. Then:
- the specified value is passed into the mechanism's `function <TransferMechanism.function>` and the function is executed
- the TransferMechanism's `value <TransferMechanism.value>` attribute is set to the output of the function
- the TransferMechanism updates is `output_states <TransferMechanism.output_states>`
A use case for `reinitialize <AdaptiveIntegrator.reinitialize>` is demonstrated in the following example:
Expand Down Expand Up @@ -227,11 +253,13 @@
... num_trials=5) #doctest: +SKIP
>>> assert np.allclose(my_time_averaged_transfer_mechanism.value, 0.72105725) #doctest: +SKIP
The integrator_function's `reinitialize <AdaptiveIntegrator.reinitialize>` property is useful in cases when the
integrator should instead start over at its original initial value or a new one. Use `reinitialize
<AdaptiveIntegrator.reinitialize>` to re-start the integrator_function's accumulation at 0.2:
The integrator_function's `reinitialize <AdaptiveIntegrator.reinitialize>` method and the TransferMechanism's
`reinitialize <TransferMechanism.reinitialize>` method are useful in cases when the integration should instead start
over at the original initial value, or a new one.
Use `reinitialize <AdaptiveIntegrator.reinitialize>` to re-start the integrator_function's accumulation at 0.2:
>>> my_time_averaged_transfer_mechanism.integrator_function.reinitialize = np.array([[0.2]]) #doctest: +SKIP
>>> my_time_averaged_transfer_mechanism.integrator_function.reinitialize(np.array([[0.2]])) #doctest: +SKIP
Run the system again to observe that my_time_averaged_transfer_mechanism's integrator_function will begin accumulating
at 0.2, following the exact same trajectory as in RUN 1:
Expand All @@ -245,6 +273,14 @@
my_time_averaged_transfer_mechanism's integrator_function effectively started RUN 3 in the same state as it began RUN 1.
As a result, it arrived at the exact same value after 5 trials (with identical inputs).
In the examples above, `reinitialize <AdaptiveIntegrator.reinitialize>` was applied directly to the integrator function.
The key difference between the `integrator_function's reinitialize <AdaptiveIntegrator.reinitialize>` and the
`TransferMechanism's reinitialize <TransferMechanism.reinitialize>` is that the latter will also execute the mechanism's
function and update its output states. This is useful if the mechanism's value or any of its output state values will
be used or checked *before* the mechanism's next execution. (This may be true if, for example, the mechanism is
`recurrent <RecurrentTransferMechanism>`, the mechanism is responsible for `modulating <ModulatorySignal_Modulation`
other components, or if a `Scheduler` condition depends on the mechanism's activity.)
COMMENT:
.. _Transfer_Examples:
Expand Down
6 changes: 1 addition & 5 deletions psyneulink/components/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,6 @@ def __init__(self,
# Required to defer assignment of self.controller by setter
# until the rest of the System has been instantiated
self.status = INITIALIZING

processes = processes or []
if not isinstance(processes, list):
processes = [processes]
Expand Down Expand Up @@ -2848,10 +2847,7 @@ def run(self,
if self.scheduler_learning is None:
self.scheduler_learning = Scheduler(graph=self.learningexecution_graph)

# initial_values = initial_values or self.initial_values
if initial_values is None and self.initial_values:
initial_values = self.initial_values

self.initial_values = initial_values

logger.debug(inputs)

Expand Down
5 changes: 4 additions & 1 deletion psyneulink/globals/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,10 +688,13 @@ def run(object,
context=context)

try:
# this will fail on processes, which do not have schedulers
object.scheduler_processing.date_last_run_end = datetime.datetime.now()
object.scheduler_learning.date_last_run_end = datetime.datetime.now()

for sched in [object.scheduler_processing, object.scheduler_learning]:
sched.clock._increment_time(TimeScale.RUN)
except AttributeError:
# this will fail on processes, which do not have schedulers
pass

# Restore learning state
Expand Down
Loading

0 comments on commit b2e3e85

Please sign in to comment.