Skip to content

Commit

Permalink
Updates to system flow
Browse files Browse the repository at this point in the history
Lerners
  now accept high level data
Planners
  now call update on all existing planner constructs on interface update
  now call render_reasoning on all planner constructs on interface render_reasoning
Complex
  added update_and_render_reasoning function
    updates and renders reasoning on each reasoning construct before the next
    passes results of previous complex reasoners to subsequent complex reasoners
  removed update and render_reason functions from interface which were no longer used
Agent
  removed render_reasoning
    it was only called locally and is no longer used
    it only rendered the complex and that responsibility is now in the complex interface
  new flow of data through ai constructs
    vehicle rep updates every knowledge construct with frame (low_level_data)
    aggregate_high_level_data adds the vehicle reps knowledge construct results (vehicle state information)
    learning systems all update with frame (low_level_data, vehicle curr data) and aggregate high_level_data
    aggregate_high_level_data adds the learning systems construct results
    planning systems all update with aggregate high_level_data
    aggregate_high_level_data adds the planning systems construct results
    complex reasoners update and render reasoning (passing results down to each in the interface) with the final result being returned by the agent
  • Loading branch information
asgibson committed Mar 22, 2024
1 parent bda906b commit 0b62cd3
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 121 deletions.
2 changes: 1 addition & 1 deletion onair/src/ai_components/learners_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, headers, _learner_plugins={}):

def update(self, low_level_data, high_level_data):
for plugin in self.learner_constructs:
plugin.update(low_level_data)
plugin.update(low_level_data, high_level_data)

def check_for_salient_event(self):
pass
Expand Down
8 changes: 6 additions & 2 deletions onair/src/ai_components/planners_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ def __init__(self, headers, _planner_plugins={}):
def update(self, high_level_data):
# Raw TLM should be transformed into high-leve state representation here
# Can store something as stale unless a planning thread is launched
pass
for plugin in self.planner_constructs:
plugin.update(high_level_data=high_level_data)

def check_for_salient_event(self):
pass

def render_reasoning(self):
pass
diagnoses = {}
for plugin in self.planner_constructs:
diagnoses[plugin.component_name] = plugin.render_reasoning()
return diagnoses
19 changes: 7 additions & 12 deletions onair/src/reasoning/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,16 @@ def __init__(self, vehicle, learners_plugin_dict, planners_plugin_dict, complex_
self.planning_systems = PlannersInterface(self.vehicle_rep.get_headers(),planners_plugin_dict)
self.complex_reasoning_systems = ComplexReasoningInterface(self.vehicle_rep.get_headers(),complex_plugin_dict)

def render_reasoning(self):
return self.complex_reasoning_systems.render_reasoning()

def reason(self, frame):
aggregate_high_level_info = {}
self.vehicle_rep.update(frame)
self.learning_systems.update(self.vehicle_rep.curr_data, self.vehicle_rep.get_state_information())
self.planning_systems.update(self.vehicle_rep.get_state_information())

aggregate_high_level_info = {'vehicle_rep' : self.vehicle_rep.get_state_information(),
'learning_systems' : self.learning_systems.render_reasoning(),
'planning_systems' : self.planning_systems.render_reasoning()}

self.complex_reasoning_systems.update(aggregate_high_level_info)
aggregate_high_level_info['vehicle_rep'] = self.vehicle_rep.get_state_information()
self.learning_systems.update(self.vehicle_rep.curr_data, aggregate_high_level_info)
aggregate_high_level_info['learning_systems'] = self.learning_systems.render_reasoning()
self.planning_systems.update(aggregate_high_level_info)
aggregate_high_level_info['planning_systems'] = self.planning_systems.render_reasoning()

return self.render_reasoning()
return self.complex_reasoning_systems.update_and_render_reasoning(aggregate_high_level_info)

def diagnose(self, time_step):
""" Grab the mnemonics from the """
Expand Down
13 changes: 6 additions & 7 deletions onair/src/reasoning/complex_reasoning_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ def __init__(self, headers, _reasoning_plugins={}):
self.headers = headers
self.reasoning_constructs = import_plugins(self.headers,_reasoning_plugins)

def update(self, high_level_data):
def update_and_render_reasoning(self, high_level_data):
intelligent_outcomes = high_level_data
intelligent_outcomes['complex_systems'] = {}
for plugin in self.reasoning_constructs:
plugin.update(high_level_data=high_level_data)
plugin.update(high_level_data=intelligent_outcomes)
intelligent_outcomes['complex_systems'] |= {plugin.component_name:plugin.render_reasoning()}
return intelligent_outcomes

def check_for_salient_event(self):
pass

def render_reasoning(self):
intelligent_outcomes = {}
for plugin in self.reasoning_constructs:
intelligent_outcomes[plugin.component_name] = plugin.render_reasoning()
return intelligent_outcomes
4 changes: 2 additions & 2 deletions test/onair/src/ai_components/test_learners_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def test_LearnersInterface_update_does_nothing_when_instance_learner_constructs_
# Assert
assert result == None

def test_LearnersInterface_update_calls_update_with_given_low_level_data_on_each_learner_constructs_item(mocker):
def test_LearnersInterface_update_calls_update_with_given_low_level_and_high_level_data_on_each_learner_constructs_item(mocker):
# Arrange
arg_low_level_data = MagicMock()
arg_high_level_data = MagicMock()
Expand All @@ -86,7 +86,7 @@ def test_LearnersInterface_update_calls_update_with_given_low_level_data_on_each
# Assert
for i in range(num_fake_learner_constructs):
assert cut.learner_constructs[i].update.call_count == 1
assert cut.learner_constructs[i].update.call_args_list[0].args == (arg_low_level_data, )
assert cut.learner_constructs[i].update.call_args_list[0].args == (arg_low_level_data, arg_high_level_data)

# check_for_salient_event
def test_LearnersInterface_salient_event_does_nothing():
Expand Down
56 changes: 52 additions & 4 deletions test/onair/src/ai_components/test_planners_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,41 @@ def test_PlannersInterface__init__sets_self_headers_to_given_headers_and_sets_se
assert cut.planner_constructs == forced_return_planner_constructs

# update tests
def test_update_does_nothing():
def test_PlannersInterface_update_does_nothing_when_instance_planner_constructs_is_empty():
# Arrange
arg_high_level_data = MagicMock()

cut = PlannersInterface.__new__(PlannersInterface)
cut.planner_constructs = []

# Act
result = cut.update(arg_high_level_data)

# Assert
assert result == None

def test_PlannersInterface_update_calls_update_with_given_low_level_and_high_level_data_on_each_planner_constructs_item(mocker):
# Arrange
arg_high_level_data = MagicMock()

cut = PlannersInterface.__new__(PlannersInterface)
cut.planner_constructs = []

num_fake_planner_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
for i in range(num_fake_planner_constructs):
cut.planner_constructs.append(MagicMock())

# Act
result = cut.update(arg_high_level_data)

# Assert
for i in range(num_fake_planner_constructs):
assert cut.planner_constructs[i].update.call_count == 1
assert cut.planner_constructs[i].update.call_args_list[0].args == ()
assert cut.planner_constructs[i].update.call_args_list[0].kwargs == {'high_level_data':arg_high_level_data}

# check_for_salient_event tests
def test_check_for_salient_event_does_nothing():
def test_PlannersInterface_check_for_salient_event_does_nothing():
# Arrange
cut = PlannersInterface.__new__(PlannersInterface)

Expand All @@ -77,12 +98,39 @@ def test_check_for_salient_event_does_nothing():
assert result == None

# render reasoning tests
def test_render_reasoning_does_nothing():
def test_PlannersInterface_render_reasoning_returns_empty_dict_when_instance_planner_constructs_is_empty(mocker):
# Arrange
cut = PlannersInterface.__new__(PlannersInterface)
cut.planner_constructs = []

# Act
result = cut.render_reasoning()

# Assert
assert result == {}


def test_PlannersInterface_render_reasoning_returns_dict_of_each_ai_construct_as_key_to_the_result_of_its_render_reasoning_when_instance_planner_constructs_is_occupied(mocker):
# Arrange
cut = PlannersInterface.__new__(PlannersInterface)
cut.planner_constructs = []

expected_result = {}

num_fake_planner_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
for i in range(num_fake_planner_constructs):
fake_ai_construct = MagicMock()
forced_return_ai_construct_render_reasoning = MagicMock()
cut.planner_constructs.append(fake_ai_construct)
mocker.patch.object(fake_ai_construct, 'render_reasoning', return_value=forced_return_ai_construct_render_reasoning)
fake_ai_construct.component_name = MagicMock()
expected_result[fake_ai_construct.component_name] = forced_return_ai_construct_render_reasoning

# Act
result = cut.render_reasoning()

# Assert
assert result == None
for i in range(num_fake_planner_constructs):
assert cut.planner_constructs[i].render_reasoning.call_count == 1
assert cut.planner_constructs[i].render_reasoning.call_args_list[0].args == ()
assert result == expected_result
64 changes: 25 additions & 39 deletions test/onair/src/reasoning/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,42 +62,27 @@ def test_Agent__init__sets_vehicle_rep_to_given_vehicle_and_learning_systems_and
assert arg_vehicle.get_bayesian_status.call_args_list[0].args == ()
assert cut.bayesian_status == fake_bayesian_status

# render_resoning tests
def test_Agent_render_reasoning_returns_call_to_complex_reasoning_systems_render_reasoning(mocker):
# Arrange
expected_result = MagicMock()
fake_complex_resoning_systems = MagicMock()

cut = Agent.__new__(Agent)
cut.complex_reasoning_systems = fake_complex_resoning_systems

mocker.patch.object(fake_complex_resoning_systems, 'render_reasoning', return_value=expected_result)

# Act
result = cut.render_reasoning()

# Assert
assert result == expected_result

# reason tests
def test_Agent_reason_updates_vehicle_rep_with_given_frame_and_updates_learning_systems_with_vehicle_curr_data_and_new_mission_status(mocker):
def test_Agent_reason_updates_vehicle_rep_with_given_frame_learners_with_frame_and_aggregated_high_level_data_planners_with_aggreagated_high_level_data_returning_complex_reasonings_update_and_render_reasoning(mocker):
# Arrange
arg_frame = MagicMock()
fake_vehicle_rep = MagicMock()
fake_vehicle_rep.curr_data = MagicMock()

# Mock and patch
fake_status = MagicMock()
fake_PDDL_state = MagicMock()
fake_state = MagicMock()
fake_vehicle_rep_state = MagicMock()
fake_learning_systems = MagicMock()
fake_planning_systems = MagicMock()
fake_complex_reasoning_systems = MagicMock()
fake_learning_systems_reasoning = MagicMock()
fake_planning_systems_reasoning = MagicMock()
expected_aggregate_high_level_info = {'vehicle_rep': fake_state,
'learning_systems':fake_learning_systems_reasoning,
'planning_systems':fake_planning_systems_reasoning}
expected_aggregate_to_learners = {'vehicle_rep': fake_vehicle_rep_state}
expected_aggregate_to_planners = {'vehicle_rep': fake_vehicle_rep_state,
'learning_systems':fake_learning_systems_reasoning}
expected_aggregate_to_complex = {'vehicle_rep': fake_vehicle_rep_state,
'learning_systems':fake_learning_systems_reasoning,
'planning_systems':fake_planning_systems_reasoning}
expected_result = MagicMock()

cut = Agent.__new__(Agent)
Expand All @@ -109,34 +94,35 @@ def test_Agent_reason_updates_vehicle_rep_with_given_frame_and_updates_learning_
mock_manager = mocker.MagicMock()

mock_manager.attach_mock(mocker.patch.object(fake_vehicle_rep, 'update'), 'cut.vehicle_rep.update')
mock_manager.attach_mock(mocker.patch.object(fake_vehicle_rep, 'get_state_information', return_value=fake_vehicle_rep_state), 'cut.vehicle_rep.get_state_information')
mock_manager.attach_mock(mocker.patch.object(fake_learning_systems, 'update'), 'cut.learning_systems.update')
mock_manager.attach_mock(mocker.patch.object(fake_learning_systems, 'render_reasoning', return_value=fake_learning_systems_reasoning), 'cut.learning_systems.render_reasoning')
mock_manager.attach_mock(mocker.patch.object(fake_planning_systems, 'update'), 'cut.planning_systems.update')
mock_manager.attach_mock(mocker.patch.object(fake_complex_reasoning_systems, 'update'), 'cut.complex_reasoning_systems.update')
mock_manager.attach_mock(mocker.patch.object(cut, 'render_reasoning', return_value=expected_result), 'cut.render_reasoning')
mock_manager.attach_mock(mocker.patch.object(fake_planning_systems, 'render_reasoning', return_value=fake_planning_systems_reasoning), 'cut.planning_systems.render_reasoning')
mock_manager.attach_mock(mocker.patch.object(fake_complex_reasoning_systems, 'update_and_render_reasoning', return_value=expected_result), 'cut.complex_reasoning_systems.update_and_render_reasoning')


mocker.patch.object(fake_vehicle_rep, 'get_state_information', side_effect=[fake_status, fake_PDDL_state, fake_state])
mocker.patch.object(fake_learning_systems, 'render_reasoning', return_value=fake_learning_systems_reasoning)
mocker.patch.object(fake_planning_systems, 'render_reasoning', return_value=fake_planning_systems_reasoning)
# mocker.patch.object(fake_learning_systems, 'render_reasoning', return_value=fake_learning_systems_reasoning)
# mocker.patch.object(fake_planning_systems, 'render_reasoning', return_value=fake_planning_systems_reasoning)

# Act
result = cut.reason(arg_frame)

# Assert
result = expected_result
#TODO: using expected_aggregate_to_complex is incorrect, appears to maybe be an issue with MagicMock somehow
# problem is its always the same object, that gets updated during the function, unfortunately it only saves the object
# not a "snapshot" of what the object was at the time, so each recorded call thinks it got the object which it did, but the state is wrong
# side_effect could be used to save the true values, but research better options
mock_manager.assert_has_calls([
mocker.call.cut.vehicle_rep.update(arg_frame),
mocker.call.cut.learning_systems.update(fake_vehicle_rep.curr_data, fake_status),
mocker.call.cut.planning_systems.update(fake_PDDL_state),
mocker.call.cut.complex_reasoning_systems.update(expected_aggregate_high_level_info),
mocker.call.cut.vehicle_rep.get_state_information(),
mocker.call.cut.learning_systems.update(fake_vehicle_rep.curr_data, expected_aggregate_to_complex),
mocker.call.cut.learning_systems.render_reasoning(),
mocker.call.cut.planning_systems.update(expected_aggregate_to_complex),
mocker.call.cut.planning_systems.render_reasoning(),
mocker.call.cut.complex_reasoning_systems.update_and_render_reasoning(expected_aggregate_to_complex),
], any_order=False)
assert cut.vehicle_rep.get_state_information.call_count == 3
assert cut.vehicle_rep.get_state_information.call_args_list[0].args == ()
assert cut.vehicle_rep.get_state_information.call_args_list[1].args == ()
assert cut.vehicle_rep.get_state_information.call_args_list[2].args == ()
assert cut.learning_systems.render_reasoning.call_count == 1
assert cut.learning_systems.render_reasoning.call_args_list[0].args == ()
assert cut.planning_systems.render_reasoning.call_count == 1
assert cut.planning_systems.render_reasoning.call_args_list[0].args == ()


# diagnose tests
Expand Down
78 changes: 24 additions & 54 deletions test/onair/src/reasoning/test_complex_resoning_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,39 +53,46 @@ def test_ComplexReasoningInterface__init__sets_self_headers_to_given_headers_and
assert complex_reasoning_interface.import_plugins.call_args_list[0].args == (arg_headers, arg__reasoning_plugins)
assert cut.reasoning_constructs == forced_return_reasoning_constructs

# update tests
def test_ComplexReasoningInterface_update_does_nothing_when_instance_reasoning_constructs_is_empty():
# update_and_render_reasoning
def test_ComplexReasoningInterface_update_and_render_reasoning_returns_given_high_level_data_with_complex_systems_as_empty_dict_when_no_reasoning_constructs(mocker):
# Arrange
arg_high_level_data = MagicMock()
fake_high_level_key = MagicMock(name='fake_high_level_key')
fake_high_level_value = MagicMock(name='fake_high_level_value')
arg_high_level_data = {fake_high_level_key:fake_high_level_value}
expected_result = arg_high_level_data | {'complex_systems':{}}

cut = ComplexReasoningInterface.__new__(ComplexReasoningInterface)
cut.reasoning_constructs = []

# Act
result = cut.update(arg_high_level_data)
result = cut.update_and_render_reasoning(arg_high_level_data)

# Assert
assert result == None
assert result == expected_result

def test_ComplexReasoningInterface_update_calls_update_with_given_low_level_data_on_each_reasoning_constructs_item(mocker):
def test_ComplexReasoningInterface_update_and_render_reasoning_invokes_on_all_reasoning_constructs_then_returns_their_results_added_to_the_hgih_level_data(mocker):
# Arrange
arg_high_level_data = MagicMock()
fake_high_level_key = MagicMock(name='fake_high_level_key')
fake_high_level_value = MagicMock(name='fake_high_level_value')
arg_high_level_data = {fake_high_level_key:fake_high_level_value}
expected_result = arg_high_level_data | {'complex_systems':{}}

cut = ComplexReasoningInterface.__new__(ComplexReasoningInterface)
cut.reasoning_constructs = []

num_fake_reasoning_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
for i in range(num_fake_reasoning_constructs):
cut.reasoning_constructs.append(MagicMock())
for i in range(0, pytest.gen.randint(1, 10)):
cut.reasoning_constructs.append(MagicMock(name=f"fake_plugin_{i}"))
cut.reasoning_constructs[-1].component_name = f"fake_plugin_{i}"
mocker.patch.object(cut.reasoning_constructs[-1], 'update')
rv = f"{i}"
mocker.patch.object(cut.reasoning_constructs[-1], 'render_reasoning', return_value=rv)
expected_result['complex_systems'] |= {cut.reasoning_constructs[-1].component_name : rv}

# Act
result = cut.update(arg_high_level_data)
result = cut.update_and_render_reasoning(arg_high_level_data)

# Assert
for i in range(num_fake_reasoning_constructs):
assert cut.reasoning_constructs[i].update.call_count == 1
assert cut.reasoning_constructs[i].update.call_args_list[0].args == ()
assert cut.reasoning_constructs[i].update.call_args_list[0].kwargs == {'high_level_data':arg_high_level_data}
assert result == expected_result
assert cut.reasoning_constructs[0].update.call_count == 1

# check_for_salient_event tests
def test_ComplexReasoningInterface_salient_event_does_nothing():
Expand All @@ -96,41 +103,4 @@ def test_ComplexReasoningInterface_salient_event_does_nothing():
result = cut.check_for_salient_event()

# Assert
assert result == None

# render_reasoning tests
def test_ComplexReasoningInterface_render_reasoning_returns_empty_dict_when_instance_reasoning_constructs_is_empty(mocker):
# Arrange
cut = ComplexReasoningInterface.__new__(ComplexReasoningInterface)
cut.reasoning_constructs = []

# Act
result = cut.render_reasoning()

# Assert
assert result == {}

def test_ComplexReasoningInterface_render_reasoning_returns_dict_of_each_ai_construct_as_key_to_the_result_of_its_render_reasoning_when_instance_reasoning_constructs_is_occupied(mocker):
# Arrange
cut = ComplexReasoningInterface.__new__(ComplexReasoningInterface)
cut.reasoning_constructs = []

expected_result = {}

num_fake_reasoning_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
for i in range(num_fake_reasoning_constructs):
fake_ai_construct = MagicMock()
forced_return_ai_construct_render_reasoning = MagicMock()
cut.reasoning_constructs.append(fake_ai_construct)
mocker.patch.object(fake_ai_construct, 'render_reasoning', return_value=forced_return_ai_construct_render_reasoning)
fake_ai_construct.component_name = MagicMock()
expected_result[fake_ai_construct.component_name] = forced_return_ai_construct_render_reasoning

# Act
result = cut.render_reasoning()

# Assert
for i in range(num_fake_reasoning_constructs):
assert cut.reasoning_constructs[i].render_reasoning.call_count == 1
assert cut.reasoning_constructs[i].render_reasoning.call_args_list[0].args == ()
assert result == expected_result
assert result == None

0 comments on commit 0b62cd3

Please sign in to comment.