From c6801b5beac93bf980d18c9de687544908060b31 Mon Sep 17 00:00:00 2001 From: Maxim Tarasov Date: Mon, 18 Dec 2023 09:56:37 -0800 Subject: [PATCH] add OpenNARS-style Distributor in Bag --- Tests/test_NAL/test_NAL2.py | 2 +- Tests/test_NAL/test_NAL5.py | 6 +++-- Tests/test_NAL/test_NAL6.py | 6 ++--- Tests/test_NAL/test_NAL7.py | 2 +- pynars/NARS/DataStructures/_py/Bag.py | 24 +++++++++++------ pynars/NARS/DataStructures/_py/Distributor.py | 26 +++++++++++++++++++ 6 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 pynars/NARS/DataStructures/_py/Distributor.py diff --git a/Tests/test_NAL/test_NAL2.py b/Tests/test_NAL/test_NAL2.py index 84d7a7e..774288c 100644 --- a/Tests/test_NAL/test_NAL2.py +++ b/Tests/test_NAL/test_NAL2.py @@ -84,7 +84,7 @@ def test_backward_inference(self): tasks_derived = process_two_premises( ' swimmer>. %1.00;0.90%', '<{?1} --> swimmer>?', - 6 + 20 ) self.assertTrue( output_contains(tasks_derived, '<{?1} --> bird>?') diff --git a/Tests/test_NAL/test_NAL5.py b/Tests/test_NAL/test_NAL5.py index 7c1f928..e9333a7 100644 --- a/Tests/test_NAL/test_NAL5.py +++ b/Tests/test_NAL/test_NAL5.py @@ -785,16 +785,18 @@ def test_conditional_deduction_compound_eliminate_0(self): tasks_derived = process_two_premises( '<(&&, [flying]>, [with_wings]>) ==> bird>>. %1.00;0.90%', ' [flying]>. %1.00;0.90%', - 2 + 10 ) self.assertTrue( output_contains(tasks_derived, '< [with_wings]> ==> bird>>. %1.00;0.81%') ) + nars.reset() + tasks_derived = process_two_premises( ' [flying]>. %1.00;0.90%', '<(&&, [flying]>, [with_wings]>) ==> bird>>. %1.00;0.90%', - 2 + 10 ) self.assertTrue( output_contains(tasks_derived, '< [with_wings]> ==> bird>>. %1.00;0.81%') diff --git a/Tests/test_NAL/test_NAL6.py b/Tests/test_NAL/test_NAL6.py index 4eb5266..0b81cb2 100644 --- a/Tests/test_NAL/test_NAL6.py +++ b/Tests/test_NAL/test_NAL6.py @@ -750,7 +750,7 @@ def test_multiple_variables_introduction_1(self): tasks_derived = process_two_premises( '(&&,<#x --> key>,<{lock1} --> (/,open,#x,_)>). %1.00;0.90%', '<{lock1} --> lock>. %1.00;0.90%', - 10 + 20 ) self.assertTrue( @@ -781,7 +781,7 @@ def test_second_variable_introduction_induction(self): tasks_derived = process_two_premises( '< (/,open,$1,_)> ==> <$1 --> key>>. %1.00;0.90%', ' lock>. %1.00;0.90%', - 10 + 20 ) self.assertTrue( @@ -864,7 +864,7 @@ def test_second_level_variable_unification_1_0(self): tasks_derived = process_two_premises( ' (&&,<#2 --> B>,C)>. %1.00;0.90%', ' B>. %1.00;0.90%', - 10 + 20 ) self.assertTrue( diff --git a/Tests/test_NAL/test_NAL7.py b/Tests/test_NAL/test_NAL7.py index 381f81c..beae182 100644 --- a/Tests/test_NAL/test_NAL7.py +++ b/Tests/test_NAL/test_NAL7.py @@ -331,7 +331,7 @@ def test_inference_on_tense_2(self): tasks_derived = process_two_premises( '<<(*,John,key_101) --> hold> =/> <(*,John,room_101) --> enter>>. %1.00;0.90%', '<(*,John,room_101) --> enter>. :\: %1.00;0.90%', - 10 + 30 ) self.assertTrue( diff --git a/pynars/NARS/DataStructures/_py/Bag.py b/pynars/NARS/DataStructures/_py/Bag.py index c511e1a..841fd98 100644 --- a/pynars/NARS/DataStructures/_py/Bag.py +++ b/pynars/NARS/DataStructures/_py/Bag.py @@ -6,6 +6,7 @@ from pynars.Narsese import Item, Task from pynars.NAL.Functions.BudgetFunctions import * from typing import Union, Callable, Any +from .Distributor import Distributor class Bag: @@ -49,11 +50,18 @@ def __init__(self, capacity: int, n_buckets: int = None, take_in_order: bool = T take_in_order (bool): if True, an item is taken out in order within a bucket, otherwise a random item is taken out. ''' self.capacity = capacity - self.pointer = 0 # Pointing to the Bag's current bucket number self.take_in_order = take_in_order self.item_lut = self.LUT(key=key) # look up table self.n_levels = n_buckets if n_buckets is not None else Config.num_buckets + self.pointer = self.n_levels - 1 # Pointing to the Bag's current bucket number + + self.distributor = Distributor.new(self.n_levels) + self.levels = tuple(list() for i in range(self.n_levels)) # initialize buckets between 0 and capacity + + self.currenCounter = 0 + self.levelIndex = capacity % self.n_levels + # self.buckets = self.Depq(maxlen=self.n_buckets) n_digits = int(math.log10(self.n_levels)) + 3 @@ -66,9 +74,12 @@ def map_priority(priority: float): def take(self, remove = True) -> Item: if len(self) == 0: return None - - if self._is_current_level_empty(): - self._move_to_next_nonempty_level() + if self._is_current_level_empty() or self.currenCounter == 0: + self.pointer = self.distributor.pick(self.levelIndex) + self.levelIndex = self.distributor.next(self.levelIndex) + while self._is_current_level_empty(): + self.pointer = self.distributor.pick(self.levelIndex) + self.levelIndex = self.distributor.next(self.levelIndex) if self.take_in_order: # take the first item from the current bucket @@ -86,10 +97,7 @@ def take(self, remove = True) -> Item: else: item = self.levels[self.pointer][idx] - bucket_probability = self.pointer / self.n_levels - rnd = random.random() # [0.0, 1.0) - if rnd > bucket_probability: - self._move_to_next_nonempty_level() + self.currenCounter = idx return item diff --git a/pynars/NARS/DataStructures/_py/Distributor.py b/pynars/NARS/DataStructures/_py/Distributor.py new file mode 100644 index 0000000..1cb4832 --- /dev/null +++ b/pynars/NARS/DataStructures/_py/Distributor.py @@ -0,0 +1,26 @@ +import functools + + +class Distributor: + @staticmethod + @functools.cache + def new(range_val): + return Distributor(range_val) + + def __init__(self, range_val): + self.capacity = (range_val * (range_val + 1)) // 2 + self.order = [-1] * self.capacity + + index = 0 + for rank in range(range_val, 0, -1): + for time in range(rank): + index = ((self.capacity // rank) + index) % self.capacity + while self.order[index] >= 0: + index = (index + 1) % self.capacity + self.order[index] = rank - 1 + + def pick(self, index): + return self.order[index] + + def next(self, index): + return (index + 1) % self.capacity \ No newline at end of file