Added SheerkaComparisonManager

This commit is contained in:
2020-05-17 20:19:26 +02:00
parent 56e0a9d338
commit 08e3086820
29 changed files with 586 additions and 148 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import time
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept
from core.sheerka.Services.SheerkaExecute import NO_MATCH
from core.sheerka.services.SheerkaExecute import NO_MATCH
from core.sheerka_logger import get_logger
from sdp.sheerkaDataProvider import Event
+48 -10
View File
@@ -12,14 +12,14 @@ from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConc
UnknownConcept
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW
from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.Services.SheerkaCreateNewConcept import SheerkaCreateNewConcept
from core.sheerka.Services.SheerkaDump import SheerkaDump
from core.sheerka.Services.SheerkaEvaluateConcept import SheerkaEvaluateConcept
from core.sheerka.Services.SheerkaExecute import SheerkaExecute
from core.sheerka.Services.SheerkaHistoryManager import SheerkaHistoryManager
from core.sheerka.Services.SheerkaModifyConcept import SheerkaModifyConcept
from core.sheerka.Services.SheerkaSetsManager import SheerkaSetsManager
from core.sheerka.Services.SheerkaVariableManager import SheerkaVariableManager
from core.sheerka.services.SheerkaCreateNewConcept import SheerkaCreateNewConcept
from core.sheerka.services.SheerkaDump import SheerkaDump
from core.sheerka.services.SheerkaEvaluateConcept import SheerkaEvaluateConcept
from core.sheerka.services.SheerkaExecute import SheerkaExecute
from core.sheerka.services.SheerkaHistoryManager import SheerkaHistoryManager
from core.sheerka.services.SheerkaModifyConcept import SheerkaModifyConcept
from core.sheerka.services.SheerkaSetsManager import SheerkaSetsManager
from core.sheerka.services.SheerkaVariableManager import SheerkaVariableManager
from core.sheerka_logger import console_handler
from printer.SheerkaPrinter import SheerkaPrinter
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
@@ -72,6 +72,8 @@ class Sheerka(Concept):
self.sdp: SheerkaDataProvider = None # SheerkaDataProvider
self.cache_manager = CacheManager(cache_only)
self.services = {} # sheerka plugins
self.builtin_cache = {} # cache for builtin concepts
self.parsers = {} # cache for builtin parsers
self.evaluators = [] # cache for builtin evaluators
@@ -114,6 +116,20 @@ class Sheerka(Concept):
def concepts_grammars(self):
return self.cache_manager.caches[self.CONCEPTS_GRAMMARS_ENTRY].cache
def bind_service_method(self, instance, method, as_name=None):
"""
Bind service method to sheerka instance for ease to use
:param instance:
:param method:
:param as_name:
:return:
"""
if as_name is None:
as_name = method.__name__
bound_method = method.__get__(instance, instance.__class__)
setattr(self, as_name, bound_method)
def initialize(self, root_folder: str = None, save_execution_context=True):
"""
Starting Sheerka
@@ -139,6 +155,7 @@ class Sheerka(Concept):
if self.sdp.first_time:
self.first_time_initialisation(exec_context)
self.initialize_services()
self.initialize_builtin_parsers()
self.initialize_builtin_evaluators()
self.initialize_builtin_concepts()
@@ -212,6 +229,21 @@ class Sheerka(Concept):
self.cache_manager.put(self.CONCEPTS_KEYS_ENTRY, self.USER_CONCEPTS_KEYS, 1000)
self.variable_handler.record(context, self.name, "save_execution_context", True)
def initialize_services(self):
"""
Introspect to find services and bind them
:return:
"""
self.init_log.debug("Initializing services")
core.utils.import_module_and_sub_module('core.sheerka.services')
base_class = "core.sheerka.services.sheerka_service.BaseService"
for service in core.utils.get_sub_classes("core.sheerka.services", base_class):
instance = service(self)
if hasattr(instance, "initialize"):
instance.initialize()
self.services[service.NAME] = instance
def initialize_builtin_concepts(self):
"""
Initializes the builtin concepts
@@ -300,8 +332,14 @@ class Sheerka(Concept):
self.cache_manager.put(self.RESOLVED_CONCEPTS_BY_FIRST_KEYWORD_ENTRY, False, res.body)
def reset(self, cache_only=False):
self.cache_manager.clear()
self.cache_manager.cache_only = cache_only
if self.cache_manager.cache_only != cache_only:
self.cache_manager.reset(cache_only)
self.initialize_caching()
for service in self.services.values():
if hasattr(service, "initialize"):
service.initialize()
else:
self.cache_manager.clear()
self.printer_handler.reset()
self.sdp.reset()
@@ -0,0 +1,206 @@
from dataclasses import dataclass
from cache.Cache import Cache
from cache.ListCache import ListCache
from core.builtin_concepts import BuiltinConcepts
from core.sheerka.services.sheerka_service import ServiceObj, BaseService
@dataclass
class ComparisonObj(ServiceObj):
"""
Order to store
"""
property: str # property to compare
a: int # id of concept a
b: int # id of concept b
op: str # comparison operation
context: str = "#" # context when the comparison is right
class SheerkaComparisonManager(BaseService):
"""
Manage partitioning of concepts
"""
NAME = "ComparisonManager"
COMPARISON_ENTRY = "Comparison"
RESOLVED_COMPARISON_ENTRY = "Resolved_Comparison"
def __init__(self, sheerka):
super().__init__(sheerka)
@staticmethod
def _compute_key(prop_name, comparison_context):
return f"{prop_name}|{comparison_context}"
@staticmethod
def _compute_weights(comparison_objs):
"""
For every element in greater_than_s, give it a weight
if weight(a) > weight(b) it means that a > b
:param comparison_objs: list of greater than objects
:return:
"""
values = {}
for comparison_obj in comparison_objs:
values[comparison_obj.a] = 1
values[comparison_obj.b] = 1
for _ in range(len(comparison_objs)):
for comparison_obj in comparison_objs:
if comparison_obj.op == ">":
values[comparison_obj.a] = values[comparison_obj.b] + 1
else:
values[comparison_obj.b] = values[comparison_obj.a] + 1
return values
@staticmethod
def _get_partition(weighted_concepts):
res = {}
for k, v in weighted_concepts.items():
res.setdefault(v, []).append(k)
return res
def _inner_add_comparison(self, comparison_obj):
key = self._compute_key(comparison_obj.property, comparison_obj.context)
previous = self.sheerka.cache_manager.get(self.COMPARISON_ENTRY, key)
new = previous.copy() if previous else []
new.append(comparison_obj)
cycles = self.detect_cycles(new)
if cycles:
concepts_in_cycle = [self.sheerka.get_by_id(c) for c in cycles]
chicken_an_egg = self.sheerka.new(BuiltinConcepts.CHICKEN_AND_EGG, body=concepts_in_cycle)
return self.sheerka.ret(self.NAME, False, chicken_an_egg)
self.sheerka.cache_manager.put(self.RESOLVED_COMPARISON_ENTRY, key, self._compute_weights(new))
self.sheerka.cache_manager.put(self.COMPARISON_ENTRY, key, comparison_obj)
return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def initialize(self):
cache = ListCache(default=lambda k: self.sheerka.sdp.get(self.COMPARISON_ENTRY, k))
self.sheerka.cache_manager.register_cache(self.COMPARISON_ENTRY, cache, True, True)
cache = Cache()
self.sheerka.cache_manager.register_cache(self.RESOLVED_COMPARISON_ENTRY, cache, persist=False)
self.sheerka.bind_service_method(self, SheerkaComparisonManager.is_greater_than)
self.sheerka.bind_service_method(self, SheerkaComparisonManager.is_less_than)
self.sheerka.bind_service_method(self, SheerkaComparisonManager.get_partition)
self.sheerka.bind_service_method(self, SheerkaComparisonManager.get_concepts_weights)
def is_greater_than(self, context, prop_name, concept_a, concept_b, comparison_context="#"):
"""
Records that the property of concept a is greater than concept b's one
:param context:
:param prop_name:
:param concept_a:
:param concept_b:
:param comparison_context:
:return:
"""
context.log(f"Setting concept {concept_a} is greater than {concept_b}", who=self.NAME)
event_digest = context.event.get_digest()
comparison_obj = ComparisonObj(event_digest, prop_name, concept_a.id, concept_b.id, ">", comparison_context)
return self._inner_add_comparison(comparison_obj)
def is_less_than(self, context, prop_name, concept_a, concept_b, comparison_context="#"):
"""
Records that the property of concept a is lesser than concept b's one
:param context:
:param prop_name:
:param concept_a:
:param concept_b:
:param comparison_context:
:return:
"""
context.log(f"Setting concept {concept_a} is less than {concept_b}", who=self.NAME)
event_digest = context.event.get_digest()
comparison_obj = ComparisonObj(event_digest, prop_name, concept_a.id, concept_b.id, "<", comparison_context)
return self._inner_add_comparison(comparison_obj)
def get_partition(self, prop_name, comparison_context="#"):
weighted_concept = self.get_concepts_weights(prop_name, comparison_context)
return self._get_partition(weighted_concept)
def get_concepts_weights(self, prop_name, comparison_context="#"):
weighted_concept = self.sheerka.cache_manager.get(
self.RESOLVED_COMPARISON_ENTRY,
self._compute_key(prop_name, comparison_context))
if weighted_concept is None:
key = self._compute_key(prop_name, comparison_context)
entries = self.sheerka.cache_manager.get(self.COMPARISON_ENTRY, key)
if entries is None:
return {}
else:
weighted_concept = self._compute_weights(entries)
self.sheerka.cache_manager.put(self.RESOLVED_COMPARISON_ENTRY, key, weighted_concept)
return weighted_concept
@staticmethod
def detect_cycles(comparison_objs):
"""
# Thanks to Divyanshu Mehta for contributing this code
# https://www.geeksforgeeks.org/detect-cycle-in-a-graph/?ref=lbp
:param comparison_objs:
:return:
"""
latest = comparison_objs[-1]
if latest.op == "=":
return None
def get_graph_and_vertices():
_graph = {}
_vertices = set()
for obj in comparison_objs:
if obj.op == "=":
continue
_vertices.add(obj.a)
_vertices.add(obj.b)
if obj.op == ">":
_graph.setdefault(obj.a, []).append(obj.b)
else:
_graph.setdefault(obj.b, []).append(obj.a)
return _graph, _vertices
def is_cyclic(v):
# Mark current node as visited and
# adds to recursion stack
visited[v] = True
rec_stack[v] = True
# Recur for all neighbours
# if any neighbour is visited and in
# recStack then graph is cyclic
if v in graph:
for neighbour in graph[v]:
if not visited[neighbour]:
if is_cyclic(neighbour):
return True
elif rec_stack[neighbour]:
return True
# The node needs to be poped from
# recursion stack before function ends
rec_stack[v] = False
return False
graph, vertices = get_graph_and_vertices()
visited = {k: False for k in vertices}
rec_stack = {k: False for k in vertices}
if is_cyclic(latest.a): # only need to check from the latest add, since the graph was not cyclic before
return [k for k, v in rec_stack.items() if v]
return None
@@ -14,7 +14,7 @@ class SheerkaCreateNewConcept:
def __init__(self, sheerka):
self.sheerka = sheerka
self.logger_name = self.create_new_concept.__name__
self.logger_name = "CreateNewConcept"
self.bnp = core.utils.get_class(BASE_NODE_PARSER_CLASS) # BaseNodeParser
def create_new_concept(self, context, concept: Concept):
@@ -11,7 +11,7 @@ CONCEPT_EVALUATION_STEPS = [
class SheerkaEvaluateConcept:
def __init__(self, sheerka):
self.sheerka = sheerka
self.logger_name = self.evaluate_concept.__name__
self.logger_name = "EvaluateConcept"
@staticmethod
def infinite_recursion_detected(context, concept):
@@ -88,7 +88,7 @@ class SheerkaEvaluateConcept:
if source.strip() == "":
concept.compiled[part_key] = DoNotResolve(source)
else:
with context.push(desc=f"Initializing compiled for {part_key}") as sub_context:
with context.push(desc=f"Initializing *compiled* for {part_key}") as sub_context:
sub_context.add_inputs(source=source)
to_parse = self.sheerka.ret(context.who, True,
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=source))
@@ -107,11 +107,11 @@ class SheerkaEvaluateConcept:
if default_value.strip() == "":
concept.compiled[var_name] = DoNotResolve(default_value)
else:
with context.push(desc=f"Initializing AST for property {var_name}") as sub_context:
with context.push(desc=f"Initializing *compiled* for property {var_name}") as sub_context:
sub_context.add_inputs(source=default_value)
to_parse = self.sheerka.ret(context.who, True,
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=default_value))
res = self.sheerka.execute(context, to_parse, steps)
res = self.sheerka.execute(sub_context, to_parse, steps)
only_success = only_successful(sub_context, res)
concept.compiled[var_name] = only_success.body.body if is_only_successful(only_success) else res
sub_context.add_values(return_values=res)
@@ -4,7 +4,7 @@ from core.builtin_concepts import BuiltinConcepts
class SheerkaModifyConcept:
def __init__(self, sheerka):
self.sheerka = sheerka
self.logger_name = self.modify_concept.__name__
self.logger_name = "ModifyConcept"
def modify_concept(self, context, concept):
old_version = self.sheerka.get_by_id(concept.id)
@@ -9,7 +9,7 @@ GROUP_PREFIX = 'All_'
class SheerkaSetsManager:
def __init__(self, sheerka):
self.sheerka = sheerka
self.logger_name = self.add_concept_to_set.__name__
self.logger_name = "SetsManager"
def set_isa(self, context, concept, concept_set):
"""
@@ -1,20 +1,21 @@
from dataclasses import dataclass
from typing import List
from core.sheerka.services.sheerka_service import ServiceObj
@dataclass
class Variable:
class Variable(ServiceObj):
"""
Variable to store
"""
event_id: str # event where the variable is modified
who: str # who is the modifier
key: str # key of the variable
value: object # value
parents: List[str] # previous references of the variable (Note that there should be only one parent)
def get_key(self):
return f"{self.who}.{self.key}"
return f"{self.who}|{self.key}"
class SheerkaVariableManager:
@@ -36,11 +37,11 @@ class SheerkaVariableManager:
self.sheerka.cache_manager.put(self.sheerka.VARIABLES_ENTRY, variable.get_key(), variable)
def load(self, who, key):
variable = self.sheerka.cache_manager.get(self.sheerka.VARIABLES_ENTRY, who + "." + key)
variable = self.sheerka.cache_manager.get(self.sheerka.VARIABLES_ENTRY, who + "|" + key)
if variable is None:
return None
return variable.value
def delete(self, context, who, key):
self.sheerka.cache_manager.delete(self.sheerka.VARIABLES_ENTRY, who + "." + key)
self.sheerka.cache_manager.delete(self.sheerka.VARIABLES_ENTRY, who + "|" + key)
@@ -0,0 +1,14 @@
from dataclasses import dataclass
@dataclass
class ServiceObj:
event_id: str # event where the object is created / modified
class BaseService:
"""
Base class for services
"""
def __init__(self, sheerka):
self.sheerka = sheerka