Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a planning interface and renamed the DDL interface to learners. … #53

Merged
merged 22 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
Data driven learning class for managing all data driven AI components
"""
import importlib.util
import importlib.util

from ..util.data_conversion import *

class DataDrivenLearning:
class LearnersInterface:
def __init__(self, headers, _ai_plugins={}):
assert(len(headers)>0), 'Headers are required'
self.headers = headers
Expand All @@ -26,21 +25,21 @@ def __init__(self, headers, _ai_plugins={}):
spec.loader.exec_module(module)
self.ai_constructs.append(module.Plugin(module_name,headers))

def apriori_training(self, batch_data):
for plugin in self.ai_constructs:
plugin.apriori_training(batch_data)

def update(self, curr_data, status):
input_data = curr_data
output_data = status_to_oneHot(status)
for plugin in self.ai_constructs:
plugin.update(input_data)

def apriori_training(self, batch_data):
for plugin in self.ai_constructs:
plugin.apriori_training(batch_data)
def check_for_salient_event(self):
pass

def render_reasoning(self):
diagnoses = {}
for plugin in self.ai_constructs:
diagnoses[plugin.component_name] = plugin.render_reasoning()
return diagnoses



37 changes: 37 additions & 0 deletions onair/src/ai_components/planners_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# GSC-19165-1, "The On-Board Artificial Intelligence Research (OnAIR) Platform"
#
# Copyright © 2023 United States Government as represented by the Administrator of
# the National Aeronautics and Space Administration. No copyright is claimed in the
# United States under Title 17, U.S. Code. All Other Rights Reserved.
#
# Licensed under the NASA Open Source Agreement version 1.3
# See "NOSA GSC-19165-1 OnAIR.pdf"

"""
Planners interface class for managing all planning-based AI components
"""
import importlib.util

from ..util.data_conversion import *

class PlannersInterface:
def __init__(self, headers, _ai_plugins={}):
assert(len(headers)>0), 'Headers are required'
self.headers = headers
self.ai_constructs = []
for module_name in list(_ai_plugins.keys()):
spec = importlib.util.spec_from_file_location(module_name, _ai_plugins[module_name])
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
self.ai_constructs.append(module.Plugin(module_name,headers))

def update(self, curr_raw_tlm, status):
# Raw TLM should be transformed into high-leve state representation here
# Can store something as stale unless a planning thread is launched
pass

def check_for_salient_event(self):
pass

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra new lines

def render_reasoning(self):
pass
14 changes: 12 additions & 2 deletions onair/src/reasoning/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,31 @@
Agent Class
Deals with supervised learning for diagnosing statuses
"""
from ..data_driven_components.data_driven_learning import DataDrivenLearning
from ..ai_components.learners_interface import LearnersInterface
from ..ai_components.planners_interface import PlannersInterface
from ..reasoning.diagnosis import Diagnosis

class Agent:
def __init__(self, vehicle, plugin_list):
self.vehicle_rep = vehicle
self.learning_systems = DataDrivenLearning(self.vehicle_rep.get_headers(),plugin_list)
self.mission_status = self.vehicle_rep.get_status()
self.bayesian_status = self.vehicle_rep.get_bayesian_status()

# AI Interfaces
self.learning_systems = LearnersInterface(self.vehicle_rep.get_headers(),plugin_list)
self.planning_systems = PlannersInterface(self.vehicle_rep.get_headers(),plugin_list)

# Markov Assumption holds
def reason(self, frame):
# Update with new telemetry
self.vehicle_rep.update(frame)
self.mission_status = self.vehicle_rep.get_status()
self.learning_systems.update(frame, self.mission_status)
self.planning_systems.update(frame, self.mission_status)

# Check for a salient event, needing acionable outcome
self.learning_systems.check_for_salient_event()
self.planning_systems.check_for_salient_event()

def diagnose(self, time_step):
""" Grab the mnemonics from the """
Expand Down
2 changes: 1 addition & 1 deletion plugins/generic/generic_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# See "NOSA GSC-19165-1 OnAIR.pdf"

import numpy as np
from onair.src.data_driven_components.ai_plugin_abstract.core import AIPlugIn
from onair.src.ai_components.ai_plugin_abstract.core import AIPlugIn

class Plugin(AIPlugIn):
def apriori_training(self,batch_data=[]):
Expand Down
2 changes: 1 addition & 1 deletion plugins/kalman_plugin/kalman_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import simdkalman
import numpy as np
from onair.src.data_driven_components.ai_plugin_abstract.core import AIPlugIn
from onair.src.ai_components.ai_plugin_abstract.core import AIPlugIn

class Plugin(AIPlugIn):
def __init__(self, name, headers, window_size=3):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import pytest
from mock import MagicMock

import onair.src.data_driven_components.ai_plugin_abstract.core as core
from onair.src.data_driven_components.ai_plugin_abstract.core import AIPlugIn
import onair.src.ai_components.ai_plugin_abstract.core as core
from onair.src.ai_components.ai_plugin_abstract.core import AIPlugIn

class FakeAIPlugIn(AIPlugIn):
def __init__(self, _name, _headers):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
# Licensed under the NASA Open Source Agreement version 1.3
# See "NOSA GSC-19165-1 OnAIR.pdf"

""" Test DataDrivenLearning Functionality """
""" Test LearnersInterface Functionality """
import pytest
from mock import MagicMock

import onair.src.data_driven_components.data_driven_learning as data_driven_learning
from onair.src.data_driven_components.data_driven_learning import DataDrivenLearning
import onair.src.ai_components.learners_interface as learners_interface
from onair.src.ai_components.learners_interface import LearnersInterface

import importlib
from typing import Dict

# __init__ tests
def test_DataDrivenLearning__init__sets_instance_headers_to_given_headers_and_does_nothing_else_when_given__ai_plugins_is_empty(mocker):
def test_LearnersInterface__init__sets_instance_headers_to_given_headers_and_does_nothing_else_when_given__ai_plugins_is_empty(mocker):
# Arrange
arg_headers = []
arg__ai_plugins = {}
Expand All @@ -27,15 +27,15 @@ def test_DataDrivenLearning__init__sets_instance_headers_to_given_headers_and_do
for i in range(num_fake_headers):
arg_headers.append(MagicMock())

cut = DataDrivenLearning.__new__(DataDrivenLearning)
cut = LearnersInterface.__new__(LearnersInterface)

# Act
cut.__init__(arg_headers, arg__ai_plugins)

# Assert
assert cut.headers == arg_headers

def test_DataDrivenLearning__init__throws_AttributeError_when_given_module_file_has_no_attribute_Plugin(mocker):
def test_LearnersInterface__init__throws_AttributeError_when_given_module_file_has_no_attribute_Plugin(mocker):
# Arrange
fake_module_name = MagicMock()
arg_headers = []
Expand All @@ -48,7 +48,7 @@ def test_DataDrivenLearning__init__throws_AttributeError_when_given_module_file_

# Assert

def test_DataDrivenLearning__init__sets_instance_ai_constructs_to_a_list_of_the_calls_AIPlugIn_with_plugin_and_given_headers_for_each_item_in_given__ai_plugins_when_given__ai_plugins_is_occupied(mocker):
def test_LearnersInterface__init__sets_instance_ai_constructs_to_a_list_of_the_calls_AIPlugIn_with_plugin_and_given_headers_for_each_item_in_given__ai_plugins_when_given__ai_plugins_is_occupied(mocker):
# Arrange
arg_headers = []
arg__ai_plugins = {}
Expand Down Expand Up @@ -77,7 +77,7 @@ def test_DataDrivenLearning__init__sets_instance_ai_constructs_to_a_list_of_the_
for i, module in enumerate(fake_module_list):
mocker.patch.object(module,'Plugin',return_value=expected_ai_constructs[i])

cut = DataDrivenLearning.__new__(DataDrivenLearning)
cut = LearnersInterface.__new__(LearnersInterface)

# Act
cut.__init__(arg_headers, arg__ai_plugins)
Expand All @@ -99,32 +99,32 @@ def test_DataDrivenLearning__init__sets_instance_ai_constructs_to_a_list_of_the_
assert cut.ai_constructs == expected_ai_constructs

# update tests
def test_DataDrivenLearning_update_only_calls_flotify_input_with_given_curr_data_and_status_to_oneHot_with_given_status_when_instance_ai_constructs_is_empty(mocker):
def test_LearnersInterface_update_only_calls_flotify_input_with_given_curr_data_and_status_to_oneHot_with_given_status_when_instance_ai_constructs_is_empty(mocker):
# Arrange
arg_curr_data = MagicMock()
arg_status = MagicMock()

mocker.patch(data_driven_learning.__name__ + '.status_to_oneHot')
mocker.patch(learners_interface.__name__ + '.status_to_oneHot')

cut = DataDrivenLearning.__new__(DataDrivenLearning)
cut = LearnersInterface.__new__(LearnersInterface)
cut.ai_constructs = []

# Act
result = cut.update(arg_curr_data, arg_status)

# Assert
assert data_driven_learning.status_to_oneHot.call_count == 1
assert data_driven_learning.status_to_oneHot.call_args_list[0].args == (arg_status,)
assert learners_interface.status_to_oneHot.call_count == 1
assert learners_interface.status_to_oneHot.call_args_list[0].args == (arg_status,)
assert result == None

def test_DataDrivenLearning_update_calls_flotify_input_with_given_curr_data_and_status_to_oneHot_with_given_status_and_calls_update_on_each_ai_construct_with_input_data_when_instance_ai_constructs_is_occupied(mocker):
def test_LearnersInterface_update_calls_flotify_input_with_given_curr_data_and_status_to_oneHot_with_given_status_and_calls_update_on_each_ai_construct_with_input_data_when_instance_ai_constructs_is_occupied(mocker):
# Arrange
arg_curr_data = MagicMock()
arg_status = MagicMock()

mocker.patch(data_driven_learning.__name__ + '.status_to_oneHot')
mocker.patch(learners_interface.__name__ + '.status_to_oneHot')

cut = DataDrivenLearning.__new__(DataDrivenLearning)
cut = LearnersInterface.__new__(LearnersInterface)
cut.ai_constructs = []

num_fake_ai_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
Expand All @@ -137,19 +137,19 @@ def test_DataDrivenLearning_update_calls_flotify_input_with_given_curr_data_and_
result = cut.update(arg_curr_data, arg_status)

# Assert
assert data_driven_learning.status_to_oneHot.call_count == 1
assert data_driven_learning.status_to_oneHot.call_args_list[0].args == (arg_status,)
assert learners_interface.status_to_oneHot.call_count == 1
assert learners_interface.status_to_oneHot.call_args_list[0].args == (arg_status,)
for i in range(num_fake_ai_constructs):
assert cut.ai_constructs[i].update.call_count == 1
assert cut.ai_constructs[i].update.call_args_list[0].args == (arg_curr_data,)
assert result == None

# apriori_training tests
def test_DataDrivenLearning_apriori_training_does_nothing_when_instance_ai_constructs_is_empty():
def test_LearnersInterface_apriori_training_does_nothing_when_instance_ai_constructs_is_empty():
# Arrange
arg_batch_data = MagicMock()

cut = DataDrivenLearning.__new__(DataDrivenLearning)
cut = LearnersInterface.__new__(LearnersInterface)
cut.ai_constructs = []

# Act
Expand All @@ -158,11 +158,11 @@ def test_DataDrivenLearning_apriori_training_does_nothing_when_instance_ai_const
# Assert
assert result == None

def test_DataDrivenLearning_apriori_training_calls_apriori_training_on_each_ai_constructs_item(mocker):
def test_LearnersInterface_apriori_training_calls_apriori_training_on_each_ai_constructs_item(mocker):
# Arrange
arg_batch_data = MagicMock()

cut = DataDrivenLearning.__new__(DataDrivenLearning)
cut = LearnersInterface.__new__(LearnersInterface)
cut.ai_constructs = []

num_fake_ai_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
Expand All @@ -178,10 +178,21 @@ def test_DataDrivenLearning_apriori_training_calls_apriori_training_on_each_ai_c
assert cut.ai_constructs[i].apriori_training.call_args_list[0].args == (arg_batch_data, )
assert result == None

# check_for_salient_event
def test_salient_event_does_nothing():
# Arrange
cut = LearnersInterface.__new__(LearnersInterface)

# Act
result = cut.check_for_salient_event()

# Assert
assert result == None

# render_reasoning tests
def test_DataDrivenLearning_render_reasoning_returns_empty_dict_when_instance_ai_constructs_is_empty(mocker):
def test_LearnersInterface_render_reasoning_returns_empty_dict_when_instance_ai_constructs_is_empty(mocker):
# Arrange
cut = DataDrivenLearning.__new__(DataDrivenLearning)
cut = LearnersInterface.__new__(LearnersInterface)
cut.ai_constructs = []

# Act
Expand All @@ -190,9 +201,9 @@ def test_DataDrivenLearning_render_reasoning_returns_empty_dict_when_instance_ai
# Assert
assert result == {}

def test_DataDrivenLearning_render_reasoning_returns_dict_of_each_ai_construct_as_key_to_the_result_of_its_render_reasoning_when_instance_ai_constructs_is_occupied(mocker):
def test_LearnersInterface_render_reasoning_returns_dict_of_each_ai_construct_as_key_to_the_result_of_its_render_reasoning_when_instance_ai_constructs_is_occupied(mocker):
# Arrange
cut = DataDrivenLearning.__new__(DataDrivenLearning)
cut = LearnersInterface.__new__(LearnersInterface)
cut.ai_constructs = []

expected_result = {}
Expand Down
Loading