diff --git a/_concepts.txt b/_concepts.txt index bc538fa..86b66da 100644 --- a/_concepts.txt +++ b/_concepts.txt @@ -1,4 +1,6 @@ #import admin + +# define numbers def concept one as 1 def concept two as 2 def concept three as 3 @@ -75,12 +77,13 @@ set_isa(one hundred, number) def concept hundreds from bnf number=n1 'hundred' 'and' number=n2 where n1 < 10 and n2 < 100 as n1 * 100 + n2 set_isa(hundreds, number) def concept hundreds from bnf number 'hundred' where number < 10 as number * 100 -last_created_concept() is number +set_isa(last_created_concept(), number) def concept thousands from bnf number 'thousand' where number < 1000 as number * 1000 set_isa(thousands, number) def concept thousands from bnf number=n1 'thousand' 'and' number=n2 as n1 * 1000 + n2 where n1 < 1000 and n2 < 1000 -last_created_concept() is number -def concept history as history() +set_isa(last_created_concept(), number) + +# define basic operations on numbers def concept plus from a plus b as a + b def concept minus from a minus b as a - b def concept multiplied from a multiplied by b as a * b @@ -89,11 +92,5 @@ set_is_greater_than(__PRECEDENCE, multiplied, plus) set_is_greater_than(__PRECEDENCE, divided, plus) set_is_greater_than(__PRECEDENCE, multiplied, minus) set_is_greater_than(__PRECEDENCE, divided, minus) -def concept precedence a > precedence b as set_is_greater_than(__PRECEDENCE, a, b) -set_isa(c:precedence a > precedence b:, __AUTO_EVAL) -def concept x is a command as set_isa(x, __AUTO_EVAL) -set_isa(c:x is a command:, __AUTO_EVAL) -def concept q from q ? as question(q) pre is_question() -set_is_lesser(__PRECEDENCE, q) -def concept x is a 'concept' as isinstance(x, Concept) pre is_question() -def concept x is a y as isa(x,y) pre is_question() + + diff --git a/_concepts_admin.txt b/_concepts_admin.txt index e0e5a02..1af4bbc 100644 --- a/_concepts_admin.txt +++ b/_concepts_admin.txt @@ -5,8 +5,34 @@ set_isa(c:explain:, __AUTO_EVAL) def concept explain last as get_last_results() | filter("id == 0") | recurse(2) set_isa(c:explain last:, __AUTO_EVAL) -def concept explain x as get_results() | filter(f"id == {x}") | recurse(3) where x +def concept explain x as get_results() | filter(f"id == {x}") | recurse(3) where isinstance(x, int) set_isa(c:explain x:, __AUTO_EVAL) -def concept explain x values where x as get_results() | filter(f"id=={x}") | format_d -set_isa(c:explain x values:, __AUTO_EVAL) \ No newline at end of file +def concept explain x values where isinstance(x, int) as get_results() | filter(f"id=={x}") | format_d +set_isa(c:explain x values:, __AUTO_EVAL) + +def concept precedence a > precedence b as set_is_greater_than(__PRECEDENCE, a, b) +set_isa(c:precedence a > precedence b:, __AUTO_EVAL) + +def concept x is a command as set_auto_eval(x, __AUTO_EVAL) +set_auto_eval(c:x is a command:) + +def concept q from q ? as question(q) pre is_question() +set_is_lesser(__PRECEDENCE, q) +set_auto_eval(c:q:) + +def concept "x is a concept" as isinstance(x, Concept) pre is_question() + +def concept x is a y as set_isa(x, y) +set_auto_eval(c:x is a y:) +def concept x is an y as set_isa(x, y) +set_auto_eval(c:x is an y:) +def concept x is a y as isa(x,y) pre is_question() +def concept x is an y as isa(x,y) pre is_question() + +def concept x has a y as set_hasa(x, y) +set_auto_eval(c:x has a y:) +def concept x has an y as set_hasa(x, y) +set_auto_eval(c:x has an y:) +def concept x has a y as hasa(x,y) pre is_question() +def concept x has an y as hasa(x,y) pre is_question() diff --git a/_concepts_lite.txt b/_concepts_lite.txt index b47c224..2de29a9 100644 --- a/_concepts_lite.txt +++ b/_concepts_lite.txt @@ -6,5 +6,4 @@ def concept apple def concept table def concept location def concept x is on y as set_attr(x, location, y) -def concept x is a y as set_isa(x, y) -def concept x is a y as isa(x, y) pre is_question() + diff --git a/src/core/builtin_concepts.py b/src/core/builtin_concepts.py index c3381e3..e363c45 100644 --- a/src/core/builtin_concepts.py +++ b/src/core/builtin_concepts.py @@ -53,6 +53,7 @@ class BuiltinConcepts(Enum): # builtin attributes ISA = "is a" # when a concept is an instance of another one + HASA = "has a" # when a concept has/owns another concept AUTO_EVAL = "auto eval" # when the concept must be auto evaluated # object @@ -74,7 +75,8 @@ class BuiltinConcepts(Enum): IS_EMPTY = "is empty" # when a set is empty NO_RESULT = "no result" # no return value returned INVALID_RETURN_VALUE = "invalid return value" # the return value of an evaluator is not correct - ALREADY_DEFINED = "already defined" # when you try to add the same object twice (a concept or whatever) + CONCEPT_ALREADY_DEFINED = "concept already defined" # when you try to add the same object twice (a concept or whatever) + PROPERTY_ALREADY_DEFINED = "property already defined" # When you try to add the same element in a property NOP = "no operation" # no operation concept. Does nothing CONCEPT_EVAL_ERROR = "concept evaluation error" # cannot evaluate a property or metadata of a concept ENUMERATION = "enum" # represents a list or a set @@ -171,7 +173,8 @@ BuiltinErrors = [str(e) for e in { BuiltinConcepts.TOO_MANY_ERRORS, BuiltinConcepts.MULTIPLE_ERRORS, BuiltinConcepts.INVALID_RETURN_VALUE, - BuiltinConcepts.ALREADY_DEFINED, + BuiltinConcepts.CONCEPT_ALREADY_DEFINED, + BuiltinConcepts.PROPERTY_ALREADY_DEFINED, BuiltinConcepts.CONCEPT_EVAL_ERROR, BuiltinConcepts.CONCEPT_ALREADY_IN_SET, BuiltinConcepts.NOT_A_SET, @@ -479,6 +482,33 @@ class ConceptAlreadyInSet(Concept): return self.get_value("concept_set") +class PropertyAlreadyDefined(Concept): + def __init__(self, property_name=None, property_value=None, concept=None): + super().__init__(BuiltinConcepts.PROPERTY_ALREADY_DEFINED, + True, + False, + BuiltinConcepts.PROPERTY_ALREADY_DEFINED) + self.set_value(ConceptParts.BODY, property_name) + self.set_value("property_value", property_value) + self.set_value("concept", concept) + self.metadata.is_evaluated = True + + def __repr__(self): + return f"PropertyAlreadyDefined(property={self.property_name}, value={self.property_value}, concept={self.concept})" + + @property + def property_name(self): + return self.body + + @property + def property_value(self): + return self.get_value("property_value") + + @property + def concept(self): + return self.get_value("concept") + + class ConditionFailed(Concept): def __init__(self, condition=None, concept=None, prop=None): super().__init__(BuiltinConcepts.CONDITION_FAILED, diff --git a/src/core/sheerka/Sheerka.py b/src/core/sheerka/Sheerka.py index 896aa4e..a34b4bb 100644 --- a/src/core/sheerka/Sheerka.py +++ b/src/core/sheerka/Sheerka.py @@ -565,7 +565,7 @@ class Sheerka(Concept): return c metadata = [(index_name, key), ("id", concept_id)] if concept_id else (index_name, key) - return self._get_unknown(metadata) + return self.get_unknown(metadata) def resolve(self, concept): """ @@ -827,20 +827,20 @@ class Sheerka(Concept): return self.parsers_prefix + name - def concepts(self): - """ - List of all known concepts (look up in sdp) - :return: - """ - res = [] - lst = self.sdp.list(self.CONCEPTS_BY_ID_ENTRY) - for item in lst: - if isinstance(item, list): - res.extend(item) - else: - res.append(item) - - return sorted(res, key=lambda i: int(i.id)) + # def concepts(self): + # """ + # List of all known concepts (look up in sdp) + # :return: + # """ + # res = [] + # lst = self.sdp.list(self.CONCEPTS_BY_ID_ENTRY) + # for item in lst: + # if isinstance(item, list): + # res.extend(item) + # else: + # res.append(item) + # + # return sorted(res, key=lambda i: int(i.id)) def get_last_execution(self): return self._last_execution @@ -896,7 +896,7 @@ class Sheerka(Concept): return a.key == b_key @staticmethod - def _get_unknown(metadata): + def get_unknown(metadata): """ Returns the concept 'UnknownConcept' for a requested id or key Note that I don't call the new() method to prevent cyclic call diff --git a/src/core/sheerka/services/SheerkaComparisonManager.py b/src/core/sheerka/services/SheerkaComparisonManager.py index 99c1ff7..b18ae46 100644 --- a/src/core/sheerka/services/SheerkaComparisonManager.py +++ b/src/core/sheerka/services/SheerkaComparisonManager.py @@ -139,7 +139,7 @@ class SheerkaComparisonManager(BaseService): co.b == comparison_obj.b and \ co.op == comparison_obj.op and \ co.context == comparison_obj.context: - return self.sheerka.ret(self.NAME, False, self.sheerka.new(BuiltinConcepts.ALREADY_DEFINED)) + return self.sheerka.ret(self.NAME, False, self.sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED)) new.append(comparison_obj) diff --git a/src/core/sheerka/services/SheerkaConceptsAlgebra.py b/src/core/sheerka/services/SheerkaConceptsAlgebra.py new file mode 100644 index 0000000..3dee820 --- /dev/null +++ b/src/core/sheerka/services/SheerkaConceptsAlgebra.py @@ -0,0 +1,194 @@ +from dataclasses import dataclass +from operator import attrgetter + +from core.builtin_concepts import BuiltinConcepts +from core.concept import Concept, ensure_concept +from core.sheerka.Sheerka import Sheerka +from core.sheerka.services.sheerka_service import BaseService + +PROPERTIES_TO_COMPUTE = [BuiltinConcepts.ISA, BuiltinConcepts.HASA] + + +@dataclass +class ConceptScore: + """ + Concept a score, relative to concept b + """ + score: float + a: Concept + b: Concept + + +class SheerkaConceptsAlgebra(BaseService): + NAME = "ConceptsAlgebra" + + def __init__(self, sheerka): + super().__init__(sheerka) + + def initialize(self): + self.sheerka.bind_service_method(self.cadd, False) + self.sheerka.bind_service_method(self.csub, False) + self.sheerka.bind_service_method(self.recognize, False) + + def cadd(self, context, *concepts): + """ + Concepts addition + Returns a concept with the union of some properties + :param context: + :param concepts: + :return: + """ + + ensure_concept(*concepts) + + res = Concept() + for c in concepts: + for prop in PROPERTIES_TO_COMPUTE: + self.add_props(res, c, prop) + + return res + + def csub(self, context, *concepts): + """ + Concepts subtraction + returns a concept where the properties of the first concept are removed if they appear in the other concepts + :param context: + :param concepts: + :return: + """ + + res = Concept() + ensure_concept(*concepts) + + if len(concepts) == 0: + return res # ? really + + # init res + for prop in PROPERTIES_TO_COMPUTE: + self.add_props(res, concepts[0], prop) + + for concept in concepts[1:]: + for prop in PROPERTIES_TO_COMPUTE: + self.sub_props(res, concept, prop) + + return res + + def add_props(self, destination, source, key): + """ + Add prop 'key', from source to destination + :param destination: + :param source: + :param key: + :return: + """ + if key not in source.metadata.props: + return + + if key in destination.metadata.props: + destination.metadata.props[key].update(source.metadata.props[key]) + else: + destination.metadata.props[key] = source.metadata.props[key].copy() + + def sub_props(self, destination, source, key): + """ + Remove the property from destination if it exists in source + :param destination: + :param source: + :param key: + :return: + """ + if key not in source.metadata.props or key not in destination.metadata.props: + return + + for item in source.metadata.props[key]: + destination.metadata.props[key].discard(item) + + def recognize(self, concept, all_scores=False): + """ + Go thru all known concepts and try to recognize the properties + :param concept: + :param all_scores: returns all positive scores if true, returns the top one otherwise + :return: + """ + + res = [] + + nb_props = self._count_props_values(concept) + if nb_props == 0: + return res + + all_concepts = self.sheerka.cache_manager.copy(Sheerka.CONCEPTS_BY_ID_ENTRY).values() \ + if self.sheerka.cache_manager.cache_only else self.sheerka.concepts() + + for c in all_concepts: + score = self._compute_score(c, concept, step_b=round(1 / nb_props, 2)) + if score > 0: + res.append(ConceptScore(score, c, concept)) + + if len(res) == 0: + props = [] + for p in [p for p in PROPERTIES_TO_COMPUTE if p in concept.metadata.props]: + props.append((p, concept.metadata.props[p])) + return self.sheerka.get_unknown(props) + + res.sort(key=attrgetter('score'), reverse=True) + if all_scores: + return res + + to_keep = [res[0]] + max_score = res[0].score + i = 1 + while i < len(res) and res[i].score == max_score: + to_keep.append(res[i]) + i += 1 + + return to_keep if len(to_keep) > 1 else to_keep[0] + + @staticmethod + def _compute_score(a, b, step_a=None, step_b=None): + """ + Compute a's score, compared to b properties + :param a: + :param b: + :param step_a: + :param step_b: + :return: + """ + score = 0 + + # adds step_b for every property that are in both a and b + for prop in PROPERTIES_TO_COMPUTE: + if prop in b.metadata.props and prop in a.metadata.props: + for prop_value in b.metadata.props[prop]: + if prop_value in a.metadata.props[prop]: + score += step_b + + if not step_a: + concept_a_nb_props = SheerkaConceptsAlgebra._count_props_values(a) + if concept_a_nb_props == 0: + return score + step_b = round(1.0 / concept_a_nb_props, 2) + + # remove step_a for every property that is in a, but not in b + for prop in PROPERTIES_TO_COMPUTE: + if prop in a.metadata.props and prop not in a.metadata.props: + score += step_a * len(a.metadata.props) + elif prop in a.metadata.props and prop in a.metadata.props: + for prop_value in a.metadata.props[prop]: + if prop_value not in b.metadata.props[prop]: + score -= step_b + + return score + + @staticmethod + def _count_props_values(concept): + """ + Count the number of values + :param concept: + :return: + """ + nb_props = 0 + for prop in PROPERTIES_TO_COMPUTE: + if prop in concept.metadata.props: + nb_props += len(concept.metadata.props[prop]) + return nb_props diff --git a/src/core/sheerka/services/SheerkaCreateNewConcept.py b/src/core/sheerka/services/SheerkaCreateNewConcept.py index cd41f19..9e5544a 100644 --- a/src/core/sheerka/services/SheerkaCreateNewConcept.py +++ b/src/core/sheerka/services/SheerkaCreateNewConcept.py @@ -44,7 +44,7 @@ class SheerkaCreateNewConcept(BaseService): return sheerka.ret( self.NAME, False, - sheerka.new(BuiltinConcepts.ALREADY_DEFINED, body=concept), + sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept), error.args[0]) # set id before saving in db diff --git a/src/core/sheerka/services/SheerkaHasAManager.py b/src/core/sheerka/services/SheerkaHasAManager.py new file mode 100644 index 0000000..cf2aca6 --- /dev/null +++ b/src/core/sheerka/services/SheerkaHasAManager.py @@ -0,0 +1,53 @@ +from core.builtin_concepts import BuiltinConcepts +from core.concept import ensure_concept +from core.sheerka.services.sheerka_service import BaseService + + +class SheerkaHasAManager(BaseService): + NAME = "HasAManager" + + def __init__(self, sheerka): + super().__init__(sheerka) + + def initialize(self): + self.sheerka.bind_service_method(self.set_hasa, True) + self.sheerka.bind_service_method(self.hasa, True) + + def set_hasa(self, context, concept_a, concept_b): + """ + Defines that concept has/owns another concept + :param context: + :param concept_a: + :param concept_b: + :return: + """ + + context.log(f"Setting concept {concept_a} has a {concept_b}", who=self.NAME) + ensure_concept(concept_a, concept_b) + + if (BuiltinConcepts.HASA in concept_a.metadata.props and + concept_b in concept_a.metadata.props[BuiltinConcepts.HASA]): + return self.sheerka.ret( + self.NAME, + False, + self.sheerka.new(BuiltinConcepts.PropertyAlreadyDefined, + body=concept_b, + name=BuiltinConcepts.HASA, + concept=concept_a)) + + concept_a.add_prop(BuiltinConcepts.HASA, concept_b) + + return self.sheerka.modify_concept(context, concept_a) + + def hasa(self, concept_a, concept_b): + """ + Check that concept 'a' has/owns concept 'b' + :param context: + :param concept_a: + :param concept_b: + :return: + """ + + ensure_concept(concept_a, concept_b) + return (BuiltinConcepts.HASA in concept_a.metadata.props and + concept_b in concept_a.metadata.props[BuiltinConcepts.HASA]) diff --git a/src/core/sheerka/services/SheerkaModifyConcept.py b/src/core/sheerka/services/SheerkaModifyConcept.py index 2eb45dd..0f4589d 100644 --- a/src/core/sheerka/services/SheerkaModifyConcept.py +++ b/src/core/sheerka/services/SheerkaModifyConcept.py @@ -44,7 +44,7 @@ class SheerkaModifyConcept(BaseService): return self.sheerka.ret( self.NAME, False, self.sheerka.new( - BuiltinConcepts.ALREADY_DEFINED, + BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept)) self.sheerka.cache_manager.update_concept(old_version, concept) diff --git a/src/core/sheerka/services/SheerkaSetsManager.py b/src/core/sheerka/services/SheerkaSetsManager.py index eaa93bb..a460ecd 100644 --- a/src/core/sheerka/services/SheerkaSetsManager.py +++ b/src/core/sheerka/services/SheerkaSetsManager.py @@ -197,7 +197,7 @@ class SheerkaSetsManager(BaseService): for c in a.metadata.props[BuiltinConcepts.ISA]: if c == b: return True - if self.isa(c, b): + if self.isa(self.sheerka.get_by_id(c.id), b): return True return False diff --git a/tests/core/test_SheerkaComparisonManager.py b/tests/core/test_SheerkaComparisonManager.py index 3af7a9e..eb21070 100644 --- a/tests/core/test_SheerkaComparisonManager.py +++ b/tests/core/test_SheerkaComparisonManager.py @@ -334,4 +334,4 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka): res = self.execution_definition(context, service, concepts_map, definition) assert not res.status - assert sheerka.isinstance(res.body, BuiltinConcepts.ALREADY_DEFINED) + assert sheerka.isinstance(res.body, BuiltinConcepts.CONCEPT_ALREADY_DEFINED) diff --git a/tests/core/test_SheerkaConceptAlgebra.py b/tests/core/test_SheerkaConceptAlgebra.py new file mode 100644 index 0000000..8d11d5a --- /dev/null +++ b/tests/core/test_SheerkaConceptAlgebra.py @@ -0,0 +1,175 @@ +from core.builtin_concepts import BuiltinConcepts +from core.concept import Concept +from core.sheerka.services.SheerkaConceptsAlgebra import ConceptScore + +from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka + + +class TestSheerkaConceptsAlgebra(TestUsingMemoryBasedSheerka): + def test_i_can_add_concepts(self): + sheerka, context, man, human, male, driver, licence, car = self.init_concepts( + "man", "human", "male", + "driver", "licence", "car") + + sheerka.set_isa(context, sheerka.new("man"), human) + sheerka.set_isa(context, sheerka.new("man"), male) + sheerka.set_hasa(context, sheerka.new("driver"), licence) + sheerka.set_hasa(context, sheerka.new("driver"), car) + + res = sheerka.cadd(context, sheerka.new("man"), sheerka.new("driver")) + + assert isinstance(res, Concept) + assert res.metadata.props == {BuiltinConcepts.ISA: {male, human}, + BuiltinConcepts.HASA: {car, licence}, } + + def test_can_add_concepts_when_property_already_exist(self): + sheerka, context, man, human, king, male = self.init_concepts( + "man", "human", "king", "male") + + sheerka.set_isa(context, sheerka.new("man"), human) + sheerka.set_isa(context, sheerka.new("king"), male) + + res = sheerka.cadd(context, sheerka.new("man"), sheerka.new("king")) + + assert isinstance(res, Concept) + assert res.metadata.props == {BuiltinConcepts.ISA: {male, human}} + + def test_i_can_subtract_concepts(self): + sheerka, context, foo, bar, isa1, isa2, hasa1, hasa2 = self.init_concepts( + "foo", "bar", + "isa1", "isa2", "has1", "has2") + + new_foo = sheerka.new("foo") + sheerka.set_isa(context, new_foo, isa1) + sheerka.set_isa(context, new_foo, isa2) + sheerka.set_hasa(context, new_foo, hasa1) + sheerka.set_hasa(context, new_foo, hasa2) + + new_bar = sheerka.new("bar") + sheerka.set_isa(context, new_bar, isa1) + sheerka.set_hasa(context, new_bar, hasa1) + + assert sheerka.csub(context) == Concept() + + res = sheerka.csub(context, new_foo) + assert isinstance(res, Concept) + assert res.metadata.props == res.metadata.props + + res = sheerka.csub(context, new_foo, new_bar) + assert isinstance(res, Concept) + assert res.metadata.props == {BuiltinConcepts.ISA: {isa2}, + BuiltinConcepts.HASA: {hasa2}, } + + def test_i_can_recognize_myself_when_cache_only_is_not_set(self): + sheerka, context, foo, isa1, hasa1, = self.init_concepts("foo", "isa1", "has1", + cache_only=False, + create_new=True) + sheerka.cache_manager.commit(context) + + new_foo = sheerka.new("foo") + sheerka.set_isa(context, new_foo, isa1) + sheerka.set_hasa(context, new_foo, hasa1) + sheerka.cache_manager.commit(context) + + assert sheerka.recognize(new_foo, all_scores=True) == [ConceptScore(1, new_foo, new_foo)] + + def test_i_can_recognize_myself_when_cache_only_is_set(self): + sheerka, context, foo, isa1, hasa1, = self.init_concepts("foo", "isa1", "has1") + + new_foo = sheerka.new("foo") + sheerka.set_isa(context, new_foo, isa1) + sheerka.set_hasa(context, new_foo, hasa1) + + assert sheerka.recognize(new_foo, all_scores=True) == [ConceptScore(1, new_foo, new_foo)] + + def test_unknown_prop_is_returned_when_nothing_is_recognized(self): + sheerka, context, isa1, hasa1, = self.init_concepts("isa1", "has1") + + foo = Concept() + foo.add_prop(BuiltinConcepts.ISA, isa1) + foo.add_prop(BuiltinConcepts.HASA, hasa1) + + res = sheerka.recognize(foo, all_scores=True) + + assert sheerka.isinstance(res, BuiltinConcepts.UNKNOWN_CONCEPT) + assert res.body == [(BuiltinConcepts.ISA, {isa1}), (BuiltinConcepts.HASA, {hasa1})] + + def test_empty_list_is_return_when_there_is_noting_to_recognize(self): + sheerka, context = self.init_concepts() + + assert sheerka.recognize(Concept(), all_scores=True) == [] + + def test_i_can_recognize_multiple_concepts_with_the_proper_score(self): + sheerka, context, foo, bar, isa1, isa2, hasa1, hasa2 = self.init_concepts( + "foo", "bar", + "isa1", "isa2", "has1", "has2") + + new_foo = sheerka.new("foo") + sheerka.set_isa(context, new_foo, isa1) + sheerka.set_isa(context, new_foo, isa2) + sheerka.set_hasa(context, new_foo, hasa1) + sheerka.set_hasa(context, new_foo, hasa2) + + new_bar = sheerka.new("bar") + sheerka.set_isa(context, new_bar, isa1) + sheerka.set_hasa(context, new_bar, hasa1) + + to_recognize = Concept() + to_recognize.add_prop(BuiltinConcepts.ISA, isa1) + to_recognize.add_prop(BuiltinConcepts.HASA, hasa1) + + res = sheerka.recognize(to_recognize, all_scores=True) + assert res == [ConceptScore(1.0, sheerka.new("bar"), to_recognize), + ConceptScore(0.5, sheerka.new("foo"), to_recognize)] + + # add the other properties, to match foo + to_recognize.add_prop(BuiltinConcepts.ISA, isa2) + to_recognize.add_prop(BuiltinConcepts.HASA, hasa2) + + res = sheerka.recognize(to_recognize, all_scores=True) + assert res == [ConceptScore(1.0, sheerka.new("foo"), to_recognize), + ConceptScore(0.5, sheerka.new("bar"), to_recognize)] + + def test_i_can_recognize_if_all_scores_is_disabled(self): + sheerka, context, foo, bar, isa1, isa2, hasa1, hasa2 = self.init_concepts( + "foo", "bar", + "isa1", "isa2", "has1", "has2") + + new_foo = sheerka.new("foo") + sheerka.set_isa(context, new_foo, isa1) + sheerka.set_isa(context, new_foo, isa2) + sheerka.set_hasa(context, new_foo, hasa1) + sheerka.set_hasa(context, new_foo, hasa2) + + new_bar = sheerka.new("bar") + sheerka.set_isa(context, new_bar, isa1) + sheerka.set_hasa(context, new_bar, hasa1) + + to_recognize = Concept() + to_recognize.add_prop(BuiltinConcepts.ISA, isa1) + to_recognize.add_prop(BuiltinConcepts.HASA, hasa1) + + res = sheerka.recognize(to_recognize, all_scores=False) + assert res == ConceptScore(1.0, sheerka.new("bar"), to_recognize) + + def test_i_can_recognize_if_all_scores_is_disabled_but_multiple_high_scores(self): + sheerka, context, foo, bar, isa1, hasa1 = self.init_concepts( + "foo", "bar", + "isa1", "has1") + + new_foo = sheerka.new("foo") + sheerka.set_isa(context, new_foo, isa1) + sheerka.set_hasa(context, new_foo, hasa1) + + new_bar = sheerka.new("bar") + sheerka.set_isa(context, new_bar, isa1) + sheerka.set_hasa(context, new_bar, hasa1) + + to_recognize = Concept() + to_recognize.add_prop(BuiltinConcepts.ISA, isa1) + to_recognize.add_prop(BuiltinConcepts.HASA, hasa1) + + res = sheerka.recognize(to_recognize, all_scores=False) + assert len(res) == 2 + assert ConceptScore(1.0, sheerka.new("foo"), to_recognize) in res + assert ConceptScore(1.0, sheerka.new("bar"), to_recognize) in res diff --git a/tests/core/test_SheerkaCreateNewConcept.py b/tests/core/test_SheerkaCreateNewConcept.py index 1c6b1cd..0d09e79 100644 --- a/tests/core/test_SheerkaCreateNewConcept.py +++ b/tests/core/test_SheerkaCreateNewConcept.py @@ -99,7 +99,7 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka): res = sheerka.create_new_concept(self.get_context(sheerka), concept) assert not res.status - assert sheerka.isinstance(res.value, BuiltinConcepts.ALREADY_DEFINED) + assert sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED) assert res.value.body == concept def test_i_can_get_a_newly_created_concept(self): @@ -260,7 +260,7 @@ class TestSheerkaCreateNewConceptFileBased(TestUsingFileBasedSheerka): res = sheerka.create_new_concept(context, concept) assert not res.status - assert sheerka.isinstance(res.value, BuiltinConcepts.ALREADY_DEFINED) + assert sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED) assert res.value.body == concept def test_new_entry_does_not_override_the_previous_ones(self): diff --git a/tests/core/test_SheerkaHasAManager.py b/tests/core/test_SheerkaHasAManager.py new file mode 100644 index 0000000..f8d62dc --- /dev/null +++ b/tests/core/test_SheerkaHasAManager.py @@ -0,0 +1,17 @@ +from core.builtin_concepts import BuiltinConcepts + +from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka + + +class TestSheerkaHasAManager(TestUsingMemoryBasedSheerka): + def test_i_can_set_hasa(self): + sheerka, context, king, kingdom = self.init_concepts("king", "kingdom") + + res = sheerka.set_hasa(context, sheerka.new("king"), kingdom) + assert res.status + + another_king = sheerka.get_by_key("king") + assert another_king.get_prop(BuiltinConcepts.HASA) == {kingdom} + + # check that the definition of the concept has been updated + assert sheerka.hasa(sheerka.new("king"), kingdom) diff --git a/tests/core/test_SheerkaModifyConcept.py b/tests/core/test_SheerkaModifyConcept.py index acdd3bc..403bbb4 100644 --- a/tests/core/test_SheerkaModifyConcept.py +++ b/tests/core/test_SheerkaModifyConcept.py @@ -73,7 +73,7 @@ class TestSheerkaModifyConcept(TestUsingMemoryBasedSheerka): res = sheerka.modify_concept(context, foo) assert not res.status - assert sheerka.isinstance(res.body, BuiltinConcepts.ALREADY_DEFINED) + assert sheerka.isinstance(res.body, BuiltinConcepts.CONCEPT_ALREADY_DEFINED) def test_i_can_modify_a_concept_that_is_in_a_list(self): sheerka, context, foo1, foo2 = self.init_concepts( diff --git a/tests/core/test_SheerkaSetsManager.py b/tests/core/test_SheerkaSetsManager.py index 1c6850b..2d0a2e7 100644 --- a/tests/core/test_SheerkaSetsManager.py +++ b/tests/core/test_SheerkaSetsManager.py @@ -241,12 +241,12 @@ class TestSheerkaSetsManager(TestUsingMemoryBasedSheerka): Concept("baz"), ) - sheerka.set_isa(context, foo, bar) - sheerka.set_isa(context, bar, baz) + sheerka.set_isa(context, sheerka.new("foo"), bar) + sheerka.set_isa(context, sheerka.new("bar"), baz) - assert sheerka.isa(foo, bar) - assert sheerka.isa(bar, baz) - assert sheerka.isa(foo, baz) + assert sheerka.isa(sheerka.new("foo"), bar) + assert sheerka.isa(sheerka.new("bar"), baz) + assert sheerka.isa(sheerka.new("foo"), baz) def test_i_cannot_manage_isa_transitivity_when_using_body(self): sheerka, context, one, another_one, number = self.init_concepts( @@ -255,9 +255,9 @@ class TestSheerkaSetsManager(TestUsingMemoryBasedSheerka): "number" ) - sheerka.set_isa(context, one, number) + sheerka.set_isa(context, sheerka.new("one"), number) - assert sheerka.isa(one, number) # sanity + assert sheerka.isa(sheerka.new("one"), number) # sanity assert not sheerka.isa(another_one, number) # Correct this misbehaviour when BuiltinConcepts.IS is implemented def test_concept_expression_recurse_id_is_updated(self): diff --git a/tests/non_reg/test_sheerka_non_reg.py b/tests/non_reg/test_sheerka_non_reg.py index ee3bd9a..068d866 100644 --- a/tests/non_reg/test_sheerka_non_reg.py +++ b/tests/non_reg/test_sheerka_non_reg.py @@ -167,7 +167,7 @@ as: assert len(res) == 1 assert not res[0].status - assert sheerka.isinstance(res[0].value, BuiltinConcepts.ALREADY_DEFINED) + assert sheerka.isinstance(res[0].value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED) @pytest.mark.parametrize("text", [ "",