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

Working Cycle Metric #87

Merged
merged 11 commits into from
May 13, 2024
3 changes: 3 additions & 0 deletions pynars/Config.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ class Config:

maximum_evidental_base_length = 20000

# cycle metrics
cycle_durations_window_length = 100
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason to keep a fixed window instead of continuously calculated average?

# calculate average
self.cycles_count += 1
self.avg_cycle_duration += (self.last_cycle_duration - self.avg_cycle_duration) / self.cycles_count

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My thinking was the variables could overflow if we don't set an upper limit to the sum/ cycles count. For example in this code the cycles_count is never capped. But I guess in practice NARS would never run for enough cycles to overflow, and it seems Python is also special in that it will infinitely extend integers so they never overflow

Copy link
Collaborator

Choose a reason for hiding this comment

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

Python is also special in that it will infinitely extend integers so they never overflow

Wow, really? That's cool, do you have a link? just for my own education ;)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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


@classmethod
def check(cls):
'''Check if each parameter is valid'''
Expand Down
6 changes: 6 additions & 0 deletions pynars/ConsolePlus.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,12 @@ def toggle_silent() -> None:
else "closed"
}.''')

@cmd_register('cycles')
def cycles(*args: List[str]) -> None:
'''Prints the "average cycles per second" metric'''
if(len(current_NARS_interface.reasoner.cycles_durations_window) == 0): current_NARS_interface.print_output(type=PrintType.INFO, content="No cycles have been run yet.")
else: current_NARS_interface.print_output(
type=PrintType.INFO, content=f'''The average cycles per second is {1 / current_NARS_interface.reasoner.avg_cycle_duration} based on the last {len(current_NARS_interface.reasoner.cycles_durations_window)} cycles. Last cycle took {current_NARS_interface.reasoner.last_cycle_duration:.32f} seconds.''')

@cmd_register(('volume'), (int, 100))
def volume(vol:int) -> None:
Expand Down
30 changes: 29 additions & 1 deletion pynars/NARS/Control/Reasoner.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from ..GlobalEval import GlobalEval



class Reasoner:

def __init__(self, n_memory, capacity, config='./config.json', nal_rules={1, 2, 3, 4, 5, 6, 7, 8, 9}) -> None:
Expand Down Expand Up @@ -50,6 +49,12 @@ def __init__(self, n_memory, capacity, config='./config.json', nal_rules={1, 2,

self.u_top_level_attention = 0.5

# metrics
self.last_cycle_duration = 0
self.cycles_durations_window = []
self.cycles_duration_window_sum = 0
self.avg_cycle_duration = 0

def reset(self):
self.memory.reset()
self.overall_experience.reset()
Expand All @@ -76,6 +81,7 @@ def input_narsese(self, text, go_cycle: bool = False) -> Tuple[bool, Union[Task,
return success, task, task_overflow

def cycle(self):
start_cycle_time_in_seconds = time()
"""Everything to do by NARS in a single working cycle"""
Global.States.reset()
tasks_derived: List[Task] = []
Expand Down Expand Up @@ -107,9 +113,14 @@ def cycle(self):
thresh_complexity = 20
tasks_derived = [
task for task in tasks_derived if task.term.complexity <= thresh_complexity]

"""done with cycle"""
self.do_cycle_metrics(start_cycle_time_in_seconds)

return tasks_derived, judgement_revised, goal_revised, answers_question, answers_quest, (
task_operation_return, task_executed)


def consider(self, tasks_derived: List[Task]):
"""
Consider a Concept in the Memory
Expand Down Expand Up @@ -272,3 +283,20 @@ def register_operator(self, name_operator: str, callback: Callable):
Operation.register(op, callback)
return op
return None

def do_cycle_metrics(self, start_cycle_time_in_seconds: float):
# record some metrics
total_cycle_duration_in_seconds = time() - start_cycle_time_in_seconds
self.last_cycle_duration = total_cycle_duration_in_seconds # store the cycle duration
# put it in with the others to find the avg duration
self.cycles_durations_window.append(total_cycle_duration_in_seconds)
self.cycles_duration_window_sum += total_cycle_duration_in_seconds

# we only want to keep track of a certain number of cycles, so remove old cycles
if len(self.cycles_durations_window) > Config.Config.cycle_durations_window_length:

self.cycles_duration_window_sum -= self.cycles_durations_window[0]
self.cycles_durations_window.pop(0)

self.avg_cycle_duration = self.cycles_duration_window_sum / len(self.cycles_durations_window) # avg seconds per 1 cycle

Loading