From 2a15f53ae9baa29ba6d4a552e2c24c1ed006e118 Mon Sep 17 00:00:00 2001 From: ccrock4t <15344554+ccrock4t@users.noreply.github.com> Date: Tue, 30 Jan 2024 07:55:49 -0500 Subject: [PATCH 1/4] Update the Priority of a Judgment Task when it becomes the best answer to a Goal/Question --- pynars/NAL/Inference/LocalRules.py | 2 +- pynars/NARS/DataStructures/_py/Memory.py | 85 +++++++++++++----------- pynars/Narsese/_py/Task.py | 8 +++ 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/pynars/NAL/Inference/LocalRules.py b/pynars/NAL/Inference/LocalRules.py index 29a14c50..5131eb50 100644 --- a/pynars/NAL/Inference/LocalRules.py +++ b/pynars/NAL/Inference/LocalRules.py @@ -43,7 +43,7 @@ def revision(task: Task, belief: Task, budget_tasklink: Budget=None, budget_term raise "Invalid case." return task -def solution_question(task: Task, belief: Belief, budget_tasklink: Budget=None, budget_termlink: Budget=None): +def solution_question(task: Task, belief: Task, budget_tasklink: Budget=None, budget_termlink: Budget=None): question: Union[Question, Quest] = task.sentence answer: Union[Judgement, Goal] = belief.sentence answer_best = question.best_answer diff --git a/pynars/NARS/DataStructures/_py/Memory.py b/pynars/NARS/DataStructures/_py/Memory.py index 1e348c99..fcd8a359 100644 --- a/pynars/NARS/DataStructures/_py/Memory.py +++ b/pynars/NARS/DataStructures/_py/Memory.py @@ -1,24 +1,20 @@ -from pynars.NAL.Functions import BudgetFunctions - +from pynars import Global from pynars.Config import Enable, Config -from pynars.NAL.Inference.LocalRules import solve_query, solution_query, solution_question +from pynars.NAL.Functions.BudgetFunctions import Budget_evaluate_goal_solution +from pynars.NAL.Functions.Tools import calculate_solution_quality +from pynars.NAL.Functions.Tools import project, project_truth +from pynars.NAL.Functions.Tools import revisible +from pynars.NAL.Inference import local__revision +from pynars.NAL.Inference.LocalRules import solution_query, solution_question from pynars.NAL.MetaLevelInference.VariableSubstitution import get_elimination__var_const - from pynars.NARS.DataStructures._py.Link import TaskLink -from pynars.Narsese._py.Sentence import Goal, Judgement, Question -from pynars.Narsese import Statement, Term, Sentence, Budget, Task, Truth +from pynars.NARS.GlobalEval import GlobalEval +from pynars.Narsese import Statement, Budget, Task +from pynars.Narsese._py.Sentence import Goal, Question from pynars.Narsese._py.Task import Belief, Desire -from .Concept import Concept from .Bag import Bag -from pynars.NAL.Functions.Tools import revisible -from pynars.NAL.Inference import local__revision -# from pynars.NARS import Operation -from pynars.NAL.Functions.Tools import project, project_truth -from pynars import Global -from pynars.NAL.Functions.BudgetFunctions import Budget_evaluate_goal_solution -from pynars.NAL.Functions.Tools import calculate_solution_quality -from typing import Callable, Any -from pynars.NARS.GlobalEval import GlobalEval +from .Concept import Concept + class Memory: def __init__(self, capacity: int, n_buckets: int = None, take_in_order: bool = False, output_buffer = None, global_eval: GlobalEval=None) -> None: @@ -299,7 +295,7 @@ def _accept_quest(self, task: Task, concept: Concept): return answers - def _solve_judgement(self, belief: Task, concept: Concept): + def _solve_judgement(self, belief_task: Task, concept: Concept): ''' It should be ensured that the task has no query-variables. @@ -309,11 +305,17 @@ def _solve_judgement(self, belief: Task, concept: Concept): ''' answers = [] # 1. try to solve yn-questions - for question in concept.question_table: - answer = solution_question(question, belief) + for question_task in concept.question_table: + question: Question = question_task.sentence + old_answer = question.best_answer + answer = solution_question(question_task, belief_task) + new_answer = question.best_answer + if old_answer != new_answer: + # the belief is a better answer to the question, so reward it + belief_task.reward_budget_priority(question_task.achieving_level()) if answer is not None: answers.append(answer) # 2. try to solve wh-questions - sub_terms = belief.term.sub_terms + sub_terms = belief_task.term.sub_terms for sub_term in sub_terms: concept_term: Concept = self.concepts.take_by_key(sub_term, remove=False) if concept_term is None: continue @@ -322,8 +324,8 @@ def _solve_judgement(self, belief: Task, concept: Concept): query = task_link.target if query is None: continue if not query.is_query: continue - if not query.term.equal(belief.term): continue - answer = solution_query(query, belief) + if not query.term.equal(belief_task.term): continue + answer = solution_query(query, belief_task) if answer is not None: answers.append(answer) return answers @@ -374,42 +376,45 @@ def _solve_query(self, query: Task, concept: Concept): raise "Invalid case." return answers - def _solve_goal(self, task: Task, concept: Concept, task_link: TaskLink=None, belief=None): + def _solve_goal(self, goal_task: Task, concept: Concept, task_link: TaskLink=None, belief_task: Task =None): ''' Args: - task (Task): Its sentence should be a goal. + goal_task (Task): Its sentence should be a goal. concept (Concept): The concept corresponding to the task. ''' tasks = [] - belief = belief or concept.match_belief(task.sentence) - if belief is None: - self.global_eval.update_satisfaction(task.achieving_level(), task.budget.priority) + belief_task = belief_task or concept.match_belief(goal_task.sentence) + if belief_task is None: + self.global_eval.update_satisfaction(goal_task.achieving_level(), goal_task.budget.priority) return tasks, None - old_best = task.best_solution + old_best = goal_task.best_solution - belief = belief or concept.match_belief(task.sentence) - if belief is None or belief == old_best: + belief_task = belief_task or concept.match_belief(goal_task.sentence) + if belief_task is None or belief_task == old_best: return tasks, None + elif belief_task != old_best: + belief_task.reward_budget_priority(goal_task.achieving_level()) + if old_best is not None: - quality_new = calculate_solution_quality(task.sentence, belief.sentence, True) - quality_old = calculate_solution_quality(task.sentence, old_best.sentence, True) + quality_new = calculate_solution_quality(goal_task.sentence, belief_task.sentence, True) + quality_old = calculate_solution_quality(goal_task.sentence, old_best.sentence, True) if (quality_new <= quality_old): - return tasks, belief + return tasks, belief_task - task.best_solution = belief - tasks.append(belief) # the task as the new best solution should be added into the internal buffer, so that it would be paid attention - budget = Budget_evaluate_goal_solution(task.sentence, belief.sentence, task.budget, (task_link.budget if task_link is not None else None)) + goal_task.best_solution = belief_task + tasks.append(belief_task) # the task as the new best solution should be added into the internal buffer, so that it would be paid attention + budget = Budget_evaluate_goal_solution(goal_task.sentence, belief_task.sentence, goal_task.budget, (task_link.budget if task_link is not None else None)) if budget.is_above_thresh: - task.budget = budget - tasks.append(task) + goal_task.budget = budget + tasks.append(goal_task) ''' Here, belief is not None, and it is the best solution for the task Thus, do global evaluation to update satisfaction of the system. ''' - self.global_eval.update_satisfaction(task.achieving_level(belief.truth), task.budget.priority) + self.global_eval.update_satisfaction(goal_task.achieving_level(belief_task.truth), goal_task.budget.priority) - return tasks, belief + return tasks, belief_task def _solve_quest(self, task: Task, concept: Concept): ''' diff --git a/pynars/Narsese/_py/Task.py b/pynars/Narsese/_py/Task.py index 3aaeedef..d54e5f89 100644 --- a/pynars/Narsese/_py/Task.py +++ b/pynars/Narsese/_py/Task.py @@ -1,11 +1,16 @@ from copy import copy from typing import Type, Union + +from pynars import NAL + from .Sentence import Sentence, Judgement, Goal, Quest, Question, Stamp from .Item import Item from .Budget import Budget from .Term import Term from .Truth import Truth + + class Task(Item): input_id = -1 best_solution: 'Task' = None @@ -35,6 +40,9 @@ def achieving_level(self, previous_belief: Truth=None): else: raise f'Invalid type! {type(self.sentence)}' + def reward_budget_priority(self, reward: float): + self.budget.priority = NAL.Functions.Or(self.budget.priority, reward) + def reduce_budget_by_achieving_level(self, belief_selected: Union[Type['Belief'], None]): truth = belief_selected.truth if belief_selected is not None else None self.budget.reduce_by_achieving_level(self.achieving_level(truth)) From f25257804e42fa589efb6570a3810ab2b7621b7f Mon Sep 17 00:00:00 2001 From: ccrock4t <15344554+ccrock4t@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:03:33 -0500 Subject: [PATCH 2/4] Reward termlinks for #75 --- pynars/NARS/DataStructures/_py/Link.py | 11 +++++------ .../InferenceEngine/GeneralEngine/GeneralEngine.py | 11 +++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pynars/NARS/DataStructures/_py/Link.py b/pynars/NARS/DataStructures/_py/Link.py index 53746401..6daaaf77 100644 --- a/pynars/NARS/DataStructures/_py/Link.py +++ b/pynars/NARS/DataStructures/_py/Link.py @@ -187,12 +187,6 @@ def get_index(cls, main_term: Union[Term, Statement, Compound], sub_term: Union[ return indices - @classmethod - def update_budget(cls, budget: Budget, q: float, p_belief: float): - budget.priority = min(1.0, Or(budget.priority, Or(q, p_belief))) - budget.durability = min(1.0-Config.budget_epsilon, Or(budget.durability, q)) - - @property def is_valid(self): @@ -211,6 +205,11 @@ def set_type(self, source_is_component=True, type: LinkType=None): Link.set_type(self, source_is_component, type) if not self.is_valid: self.type = None + + def reward_budget(self, reward: float): + self.budget.quality = Or(self.budget.quality, reward) + + @property def is_valid(self): return self.type in ( diff --git a/pynars/NARS/InferenceEngine/GeneralEngine/GeneralEngine.py b/pynars/NARS/InferenceEngine/GeneralEngine/GeneralEngine.py index 56eba53a..87b036f9 100644 --- a/pynars/NARS/InferenceEngine/GeneralEngine/GeneralEngine.py +++ b/pynars/NARS/InferenceEngine/GeneralEngine/GeneralEngine.py @@ -391,10 +391,13 @@ def step(self, concept: Concept): if is_valid: Global.States.record_premises(task, belief) Global.States.record_rules(rules) - tasks = self.inference(task, belief, term_belief, task_link_valid, term_link_valid, rules) - if term_link_valid is not None: # TODO: Check here whether the budget updating is the same as OpenNARS 3.0.4. - for task in tasks: TermLink.update_budget(term_link_valid.budget, task.budget.quality, belief.budget.priority if belief is not None else concept_target.budget.priority) - + derived_tasks = self.inference(task, belief, term_belief, task_link_valid, term_link_valid, rules) + if term_link_valid is not None: + # reward the termlink + for derived_task in derived_tasks: + reward: float = max(derived_task.budget.priority, task.achieving_level()) + TermLink.reward_budget(reward) + tasks_derived.extend(tasks) for term_link in term_links: concept.term_links.put_back(term_link) From 1e6dfd2cc213d40275dde855303cd02a40bc057e Mon Sep 17 00:00:00 2001 From: ccrock4t <15344554+ccrock4t@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:07:06 -0500 Subject: [PATCH 3/4] Update GeneralEngine.py --- .../InferenceEngine/GeneralEngine/GeneralEngine.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pynars/NARS/InferenceEngine/GeneralEngine/GeneralEngine.py b/pynars/NARS/InferenceEngine/GeneralEngine/GeneralEngine.py index 87b036f9..be361426 100644 --- a/pynars/NARS/InferenceEngine/GeneralEngine/GeneralEngine.py +++ b/pynars/NARS/InferenceEngine/GeneralEngine/GeneralEngine.py @@ -391,14 +391,14 @@ def step(self, concept: Concept): if is_valid: Global.States.record_premises(task, belief) Global.States.record_rules(rules) - derived_tasks = self.inference(task, belief, term_belief, task_link_valid, term_link_valid, rules) + new_tasks = self.inference(task, belief, term_belief, task_link_valid, term_link_valid, rules) if term_link_valid is not None: # reward the termlink - for derived_task in derived_tasks: - reward: float = max(derived_task.budget.priority, task.achieving_level()) - TermLink.reward_budget(reward) + for new_task in new_tasks: + reward: float = max(new_task.budget.priority, task.achieving_level()) + term_link_valid.reward_budget(reward) - tasks_derived.extend(tasks) + tasks_derived.extend(new_tasks) for term_link in term_links: concept.term_links.put_back(term_link) From 569883752e25043d6fc76bef369db15814e68cbe Mon Sep 17 00:00:00 2001 From: ccrock4t <15344554+ccrock4t@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:20:01 -0500 Subject: [PATCH 4/4] Reward tasklinks like tasks, for #77 --- pynars/NARS/DataStructures/_py/Link.py | 5 ++++- pynars/NARS/DataStructures/_py/Memory.py | 13 +++++++++---- pynars/Narsese/_py/Task.py | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pynars/NARS/DataStructures/_py/Link.py b/pynars/NARS/DataStructures/_py/Link.py index 6daaaf77..51bc8220 100644 --- a/pynars/NARS/DataStructures/_py/Link.py +++ b/pynars/NARS/DataStructures/_py/Link.py @@ -239,12 +239,15 @@ def __init__(self, source: 'Concept', target: 'Concept', budget: Budget, copy_bu def set_type(self, source_is_component=True, type: LinkType=None): Link.set_type(self, source_is_component, type, enable_transform=True) if not self.is_valid: self.type = None + + def reward_budget(self, reward: float): + self.budget.priority = Or(self.budget.priority, reward) @property def is_valid(self) -> bool: return self.type in ( LinkType.SELF, - LinkType.COMPOUND, + LinkType.COMPOUND, LinkType.COMPOUND_STATEMENT, LinkType.COMPOUND_CONDITION, LinkType.TRANSFORM, diff --git a/pynars/NARS/DataStructures/_py/Memory.py b/pynars/NARS/DataStructures/_py/Memory.py index fcd8a359..827fb9b9 100644 --- a/pynars/NARS/DataStructures/_py/Memory.py +++ b/pynars/NARS/DataStructures/_py/Memory.py @@ -311,8 +311,11 @@ def _solve_judgement(self, belief_task: Task, concept: Concept): answer = solution_question(question_task, belief_task) new_answer = question.best_answer if old_answer != new_answer: - # the belief is a better answer to the question, so reward it - belief_task.reward_budget_priority(question_task.achieving_level()) + # the belief is a better answer to the question, so reward the Task and tasklinks + reward = question_task.achieving_level() + belief_task.reward_budget(reward) + for task_link in concept.task_links: + task_link.reward_budget(reward) if answer is not None: answers.append(answer) # 2. try to solve wh-questions sub_terms = belief_task.term.sub_terms @@ -393,8 +396,10 @@ def _solve_goal(self, goal_task: Task, concept: Concept, task_link: TaskLink=Non if belief_task is None or belief_task == old_best: return tasks, None elif belief_task != old_best: - belief_task.reward_budget_priority(goal_task.achieving_level()) - + reward = goal_task.achieving_level() + belief_task.reward_budget(reward) + for task_link in concept.task_links: + task_link.reward_budget(reward) if old_best is not None: quality_new = calculate_solution_quality(goal_task.sentence, belief_task.sentence, True) diff --git a/pynars/Narsese/_py/Task.py b/pynars/Narsese/_py/Task.py index d54e5f89..4c16e70a 100644 --- a/pynars/Narsese/_py/Task.py +++ b/pynars/Narsese/_py/Task.py @@ -40,7 +40,7 @@ def achieving_level(self, previous_belief: Truth=None): else: raise f'Invalid type! {type(self.sentence)}' - def reward_budget_priority(self, reward: float): + def reward_budget(self, reward: float): self.budget.priority = NAL.Functions.Or(self.budget.priority, reward) def reduce_budget_by_achieving_level(self, belief_selected: Union[Type['Belief'], None]):