Skip to content

Commit

Permalink
Fix/system/learning ends before terminal (#685)
Browse files Browse the repository at this point in the history
* • System
  _instantiate_learning_graph(): fixed bug in which learning failed to
    be instantiated for a System in which the last Mechanism in a
    learning sequence was not the TERMINAL Mechanism of the System.

* • System
  _instantiate_learning_graph(): fixed bug in which learning failed to
    be instantiated for a System in which the last Mechanism in a
    learning sequence was not the TERMINAL Mechanism of the System.

* • Scripts/Examples
  Rumelhart Semantic Network: updated with step function
  • Loading branch information
jdcpni authored Mar 2, 2018
1 parent ba8dacb commit a75aeb0
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 28 deletions.
39 changes: 37 additions & 2 deletions Scripts/Examples/Rumelhart Semantic Network.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
# At present, it implements only the structure of the network, as shown below:

# Semantic Network:
# _
# _
# R_STEP P_STEP Q_STEP A_STEP | Readout Processes
# | | / / _______|
# REP PROP QUAL ACT |
# \___\__/____/ |
# | _ | Output Processes
Expand All @@ -21,6 +23,16 @@

# It does not yet implement learning or testing.


def step(variable,params,context):
if np.sum(variable)<.5:
out=0
else:
out=1
return(out)
Step=pnl.UserDefinedFunction(custom_function=step,
default_variable=np.zeros(4))

#Processing Units:
rep_in = pnl.TransferMechanism(size=10, name='REP_IN')
rel_in = pnl.TransferMechanism(size=11, name='REL_IN')
Expand All @@ -30,6 +42,10 @@
prop_out = pnl.TransferMechanism(size=12, function=pnl.Logistic, name='PROP_OUT')
qual_out = pnl.TransferMechanism(size=13, function=pnl.Logistic, name='QUAL_OUT')
act_out = pnl.TransferMechanism(size=14, function=pnl.Logistic, name='ACT_OUT')
r_step = pnl.ProcessingMechanism(size=10, function=Step, name='REP_STEP')
p_step = pnl.ProcessingMechanism(size=12, function=Step, name='PROP_STEP')
q_step = pnl.ProcessingMechanism(size=13, function=Step, name='QUAL_STEP')
a_step = pnl.ProcessingMechanism(size=14, function=Step, name='ACT_STEP')

#Processes that comprise the System:
# NOTE: this is one of several configuration of processes that can be used to construct the full network
Expand All @@ -53,8 +69,27 @@
learning=pnl.LEARNING,
name='REL_ACT_PROC')

rep_step_proc = pnl.Process(pathway=[rep_out, r_step],
name='REP_STEP_PROC')
act_step_proc = pnl.Process(pathway=[act_out, a_step],
name='ACT_STEP_PROC')
qual_step_proc = pnl.Process(pathway=[qual_out, q_step],
name='QUAL_STEP_PROC')
prop_step_proc = pnl.Process(pathway=[prop_out, p_step],
name='PROP_STEP_PROC')


# The System:
S = pnl.System(processes=[rep_hidden_proc, rel_hidden_proc, rel_rep_proc, rel_prop_proc, rel_qual_proc, rel_act_proc])
S = pnl.System(processes=[rep_hidden_proc,
rel_hidden_proc,
rel_rep_proc,
rel_prop_proc,
rel_qual_proc,
rel_act_proc,
rep_step_proc,
act_step_proc,
qual_step_proc,
prop_step_proc])

# Shows just the processing network:
# S.show_graph(show_dimensions=True)
Expand Down
66 changes: 40 additions & 26 deletions psyneulink/components/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,7 @@ def _instantiate_learning_graph(self, context=None):
self.learningGraph = OrderedDict()
self.learning_execution_graph = OrderedDict()


def build_dependency_sets_by_traversing_projections(sender_mech, process):

# MappingProjections are legal recipients of learning projections (hence the call)
Expand All @@ -1606,25 +1607,26 @@ def build_dependency_sets_by_traversing_projections(sender_mech, process):

# If sender_mech is an ObjectiveMechanism, and:
# - none of the Mechanisms that project to it are are a TERMINAL Mechanism for the current Process, or
# - all of the Mechanisms that project to it already have an ObjectiveMechanism, then:
# - do not include the ObjectiveMechanism in the graph;
# - be sure that its outputState projects to the ERROR_SIGNAL inputState of a LearningMechanism
# (labelled "learning_mech" here -- raise an exception if it does not;
# - determine whether learning_mech's ERROR_SIGNAL inputState receives any other projections
# from another ObjectiveMechanism or LearningMechanism (labelled "error_signal_projection" here)
# -- if it does, be sure that it is from the same system and if so return;
# (note: this shouldn't be true, but the test is here for completeness and sanity-checking)
# - if learning_mech's ERROR_SIGNAL inputState does not receive any projections from
# another objectiveMechanism and/or LearningMechanism in the system, then:
# - find the sender to the ObjectiveMechanism (labelled "error_source" here)
# - find the 1st projection from error_source that projects to the ACTIVATION_INPUT inputState of
# a LearningMechanism (labelled "error_signal" here)
# - instantiate a MappingProjection from error_signal to learning_mech
# projected
# - all of the Mechanisms that project to it already have an ObjectiveMechanism,
# Then:
# - do not include the ObjectiveMechanism in the graph;
# - be sure that its outputState projects to the ERROR_SIGNAL inputState of a LearningMechanism
# (labelled "learning_mech" here -- raise an exception if it does not;
# - determine whether learning_mech's ERROR_SIGNAL inputState receives any other projections
# from another ObjectiveMechanism or LearningMechanism (labelled "error_signal_projection" here)
# -- if it does, be sure that it is from the same system and if so return;
# (note: this shouldn't be true, but the test is here for completeness and sanity-checking)
# - if learning_mech's ERROR_SIGNAL inputState does not receive any projections from
# another objectiveMechanism and/or LearningMechanism in the system, then:
# - find the sender to the ObjectiveMechanism (labelled "error_source" here)
# - find the 1st projection from error_source that projects to the ACTIVATION_INPUT inputState of
# a LearningMechanism (labelled "error_signal" here)
# - instantiate a MappingProjection from error_signal to learning_mech
# projected
# IMPLEMENTATION NOTE: Composition should allow 1st condition if user indicates internal TARGET is desired;
# for now, however, assume this is not desired (i.e., only TERMINAL mechanisms
# should project to ObjectiveMechanisms) and always replace internal
# ObjectiveMechanism with projection from a LearningMechanism (if it is available)
# for now, however, assume this is not desired (i.e., only TERMINAL mechanisms
# should project to ObjectiveMechanisms) and always replace internal
# ObjectiveMechanism with projection from a LearningMechanism (if it is available)

obj_mech_replaced = False

Expand All @@ -1646,13 +1648,21 @@ def build_dependency_sets_by_traversing_projections(sender_mech, process):
if sample_mech != learning_mech.output_source:
assert False

# Its the 1st item in the learning_execution_graph, so could be for a TERMINAL Mechanism of the System
# ObjectiveMechanism the 1st item in the learning_execution_graph, so could be for:
# - the last Mechanism in a learning sequence, or
# - a TERMINAL Mechanism of the System
if len(self.learning_execution_graph) == 0:

# If sample_mech is NOT for a TERMINAL Mechanism of the current System,
# - obj_mech should NOT be included in the learning_execution_graph and
# - should be replaced with appropriate projections to sample_mechs's afferent LearningMechanisms
if not sample_mech.systems[self] is TERMINAL:
# If is the last item in a learning sequence,
# doesn't matter if it is a TERMINAL Mechanism; needs to remain as a Target for the System
if not any(proj.has_learning_projection and self in proj.receiver.owner.systems
for proj in sample_mech.output_state.efferents):
pass
# If sample_mech is:
# - NOT for a TERMINAL Mechanism of the current System
# Then:
# - obj_mech should NOT be included in the learning_execution_graph and
# - should be replaced with appropriate projections to sample_mechs's afferent LearningMechanisms
elif not sample_mech.systems[self] is TERMINAL:
_assign_error_signal_projections(sample_mech, self, obj_mech)
# Don't process ObjectiveMechanism any further (since its been replaced)
return
Expand Down Expand Up @@ -1683,9 +1693,13 @@ def build_dependency_sets_by_traversing_projections(sender_mech, process):

# INTERNAL CONVERGENCE
# None of the mechanisms that project to it are a TERMINAL mechanism
elif not all(all(projection.sender.owner.processes[proc] is TERMINAL
elif (not all(all(projection.sender.owner.processes[proc] is TERMINAL
for proc in projection.sender.owner.processes)
for projection in obj_mech.input_states[SAMPLE].path_afferents):
for projection in obj_mech.input_states[SAMPLE].path_afferents)
# and it is not for the last Mechanism in a learning sequence
and any(proj.has_learning_projection and self in proj.receiver.owner.systems
for proj in sample_mech.output_state.efferents)
):

_assign_error_signal_projections(sample_mech, self, obj_mech)
obj_mech_replaced = True
Expand Down

0 comments on commit a75aeb0

Please sign in to comment.