Fixed #100 : SheerkaAdmin: Add builtins() command
Fixed #99 : SheerkaQueryManager: I can manage contains predicate when filtering objects Fixed #97 : ERROR: list indices must be integers or slices, not Concept Fixed #96 : SequenceNodeParser: SequenceNodeParser must correctly handle concept definition Fixed #95 : ResolveAmbiguity must not remove concepts that do not require evaluation Fixed #94 : Concepts with the same key are lost when new ontology Fixed #93 : Introduce BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED Fixed #92 : ExpressionParser: Implement compile_disjunctions() Fixed #91 : Implement get_concepts_complexity(context, concepts, concept_parts) Fixed #90 : ResolveAmbiguity : where predicate is not used to resolve ambiguity Fixed #89 : ResolveAmbiguityEvaluator: Concepts embedded in ConceptNode are not resolved Fixed #88: SyaNodeParser: Parse multiple parameters when some of the are not recognized Fixed #87: SyaNodeParser : Parse the multiple parameters
This commit is contained in:
+73
-10
@@ -7,8 +7,10 @@ from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts, DEFINITION_TYPE_BNF, concept_part_value
|
||||
from core.global_symbols import NotInit, NotFound, INIT_AST_PARSERS, DEFAULT_EVALUATORS
|
||||
from core.rule import Rule
|
||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||
from core.tokenizer import Tokenizer, TokenKind
|
||||
from core.utils import as_bag
|
||||
from parsers.BaseExpressionParser import compile_disjunctions, AndNode
|
||||
from parsers.BaseNodeParser import SourceCodeNode, ConceptNode, UnrecognizedTokensNode, SourceCodeWithConceptNode, \
|
||||
RuleNode, LexerNode
|
||||
from parsers.BaseParser import ParsingError
|
||||
@@ -205,7 +207,7 @@ def only_successful(context, return_values):
|
||||
def resolve_ambiguity(context, concepts):
|
||||
"""
|
||||
From the list of concepts, elect the one(s) that best suit(s) the context
|
||||
Use the PRE metadata to choose the correct concepts
|
||||
Use the PRE and WHERE metadata to choose the correct concepts
|
||||
:param context:
|
||||
:param concepts:
|
||||
:return:
|
||||
@@ -214,8 +216,9 @@ def resolve_ambiguity(context, concepts):
|
||||
# we first sort by condition complexity. The more complex is the PRE condition, the more likely
|
||||
# the concept matches the context
|
||||
by_complexity = {}
|
||||
parts = [concept_part_value(ConceptParts.PRE), concept_part_value(ConceptParts.WHERE)]
|
||||
for c in concepts:
|
||||
by_complexity.setdefault(get_condition_complexity(c, concept_part_value(ConceptParts.PRE)), []).append(c)
|
||||
by_complexity.setdefault(get_concept_complexity(context, c, parts), []).append(c)
|
||||
|
||||
remaining_concepts = []
|
||||
for complexity in sorted(by_complexity.keys(), reverse=True):
|
||||
@@ -226,14 +229,14 @@ def resolve_ambiguity(context, concepts):
|
||||
evaluated = context.sheerka.evaluate_concept(context, c,
|
||||
eval_body=False,
|
||||
validation_only=True,
|
||||
metadata=[ConceptParts.PRE])
|
||||
metadata=[ConceptParts.PRE, ConceptParts.WHERE])
|
||||
if context.sheerka.is_success(evaluated) or evaluated.key == c.key:
|
||||
remaining_concepts.append(c)
|
||||
|
||||
if len(remaining_concepts) > 0:
|
||||
break # no need to check concept with lower complexity
|
||||
|
||||
if len(remaining_concepts) in (0, 1):
|
||||
if len(remaining_concepts) < 2:
|
||||
return remaining_concepts # they all failed the pre conditions or one champ is found
|
||||
|
||||
# for concepts with the same condition complexity, we choose the one that has the less number of variables
|
||||
@@ -246,19 +249,58 @@ def resolve_ambiguity(context, concepts):
|
||||
return by_number_of_vars[min(by_number_of_vars.keys())]
|
||||
|
||||
|
||||
def get_condition_complexity(concept, concept_part_str):
|
||||
def get_condition_complexity(context, condition):
|
||||
if condition is None or condition.strip() == "":
|
||||
return 0
|
||||
|
||||
# # count the number of conjunctions
|
||||
from parsers.LogicalOperatorParser import LogicalOperatorParser
|
||||
parser = LogicalOperatorParser()
|
||||
res = parser.parse(context, ParserInput(condition))
|
||||
if not res.status:
|
||||
return 0
|
||||
|
||||
disjunctions = compile_disjunctions(res.body.body)
|
||||
complexity = 0
|
||||
for conjunction in disjunctions:
|
||||
node_complexity = len(conjunction.parts) if isinstance(conjunction, AndNode) else 1
|
||||
if node_complexity > complexity:
|
||||
complexity = node_complexity
|
||||
|
||||
return complexity
|
||||
|
||||
|
||||
def get_concept_complexity(context, concept, concepts_parts):
|
||||
"""
|
||||
Need to find a proper algorithm to compute the complexity of a concept metadata
|
||||
So far, the concept is considered as complex if it has concept_part_str (so far with concept_part_str='pre')
|
||||
:param context:
|
||||
:param concept:
|
||||
:param concept_part_str:
|
||||
:param concepts_parts:
|
||||
:return:
|
||||
"""
|
||||
value = getattr(concept.get_metadata(), concept_part_str)
|
||||
if value is None or value.strip() == 0:
|
||||
return 0
|
||||
complexity = 0
|
||||
for i, parts in enumerate(reversed(concepts_parts)):
|
||||
|
||||
return 1 # no real computing as of now
|
||||
for part in parts.split("|"):
|
||||
value = getattr(concept.get_metadata(), part)
|
||||
part_complexity = get_condition_complexity(context, value)
|
||||
|
||||
if part_complexity > 0:
|
||||
complexity += part_complexity + (i * 100)
|
||||
|
||||
return complexity
|
||||
|
||||
|
||||
def get_concepts_complexity(context, concepts, concepts_parts):
|
||||
"""
|
||||
compute the complexity of the concepts, relatively to each others
|
||||
:param context:
|
||||
:param concepts: concepts
|
||||
:param concepts_parts: metadata to use to compute the complexity
|
||||
:return:
|
||||
"""
|
||||
return {c.id or c.name: get_concept_complexity(context, c, concepts_parts) for c in concepts}
|
||||
|
||||
|
||||
def only_parsers_results(context, return_values):
|
||||
@@ -723,6 +765,27 @@ def set_is_evaluated(concepts, check_nb_variables=False):
|
||||
concepts.get_hints().is_evaluated = True
|
||||
|
||||
|
||||
def update_concepts_hints(concepts, is_evaluated=None, recognized_by=None, is_instance=None):
|
||||
if concepts is None:
|
||||
return
|
||||
|
||||
def update_concept_hints(c, _is_evaluated, _recognized_by, _is_instance):
|
||||
if _is_evaluated is not None:
|
||||
c.get_hints().is_evaluated = _is_evaluated
|
||||
|
||||
if _recognized_by is not None:
|
||||
c.get_hints().recognized_by = _recognized_by
|
||||
|
||||
if _is_instance is not None:
|
||||
c.get_hints().is_instance = _is_instance
|
||||
|
||||
if hasattr(concepts, "__iter__"):
|
||||
for concept in concepts:
|
||||
update_concept_hints(concept, is_evaluated, recognized_by, is_instance)
|
||||
else:
|
||||
update_concept_hints(concepts, is_evaluated, recognized_by, is_instance)
|
||||
|
||||
|
||||
def ensure_concept(*concepts):
|
||||
if hasattr(concepts, "__iter__"):
|
||||
for concept in concepts:
|
||||
|
||||
Reference in New Issue
Block a user