Introduced ConceptsAlgebra
This commit is contained in:
+8
-11
@@ -1,4 +1,6 @@
|
|||||||
#import admin
|
#import admin
|
||||||
|
|
||||||
|
# define numbers
|
||||||
def concept one as 1
|
def concept one as 1
|
||||||
def concept two as 2
|
def concept two as 2
|
||||||
def concept three as 3
|
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
|
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)
|
set_isa(hundreds, number)
|
||||||
def concept hundreds from bnf number 'hundred' where number < 10 as number * 100
|
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
|
def concept thousands from bnf number 'thousand' where number < 1000 as number * 1000
|
||||||
set_isa(thousands, number)
|
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
|
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
|
set_isa(last_created_concept(), number)
|
||||||
def concept history as history()
|
|
||||||
|
# define basic operations on numbers
|
||||||
def concept plus from a plus b as a + b
|
def concept plus from a plus b as a + b
|
||||||
def concept minus from a minus 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
|
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, divided, plus)
|
||||||
set_is_greater_than(__PRECEDENCE, multiplied, minus)
|
set_is_greater_than(__PRECEDENCE, multiplied, minus)
|
||||||
set_is_greater_than(__PRECEDENCE, divided, 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()
|
|
||||||
|
|||||||
+28
-2
@@ -5,8 +5,34 @@ set_isa(c:explain:, __AUTO_EVAL)
|
|||||||
def concept explain last as get_last_results() | filter("id == 0") | recurse(2)
|
def concept explain last as get_last_results() | filter("id == 0") | recurse(2)
|
||||||
set_isa(c:explain last:, __AUTO_EVAL)
|
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)
|
set_isa(c:explain x:, __AUTO_EVAL)
|
||||||
|
|
||||||
def concept explain x values where x as get_results() | filter(f"id=={x}") | format_d
|
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)
|
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()
|
||||||
|
|||||||
+1
-2
@@ -6,5 +6,4 @@ def concept apple
|
|||||||
def concept table
|
def concept table
|
||||||
def concept location
|
def concept location
|
||||||
def concept x is on y as set_attr(x, location, y)
|
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()
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class BuiltinConcepts(Enum):
|
|||||||
|
|
||||||
# builtin attributes
|
# builtin attributes
|
||||||
ISA = "is a" # when a concept is an instance of another one
|
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
|
AUTO_EVAL = "auto eval" # when the concept must be auto evaluated
|
||||||
|
|
||||||
# object
|
# object
|
||||||
@@ -74,7 +75,8 @@ class BuiltinConcepts(Enum):
|
|||||||
IS_EMPTY = "is empty" # when a set is empty
|
IS_EMPTY = "is empty" # when a set is empty
|
||||||
NO_RESULT = "no result" # no return value returned
|
NO_RESULT = "no result" # no return value returned
|
||||||
INVALID_RETURN_VALUE = "invalid return value" # the return value of an evaluator is not correct
|
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
|
NOP = "no operation" # no operation concept. Does nothing
|
||||||
CONCEPT_EVAL_ERROR = "concept evaluation error" # cannot evaluate a property or metadata of a concept
|
CONCEPT_EVAL_ERROR = "concept evaluation error" # cannot evaluate a property or metadata of a concept
|
||||||
ENUMERATION = "enum" # represents a list or a set
|
ENUMERATION = "enum" # represents a list or a set
|
||||||
@@ -171,7 +173,8 @@ BuiltinErrors = [str(e) for e in {
|
|||||||
BuiltinConcepts.TOO_MANY_ERRORS,
|
BuiltinConcepts.TOO_MANY_ERRORS,
|
||||||
BuiltinConcepts.MULTIPLE_ERRORS,
|
BuiltinConcepts.MULTIPLE_ERRORS,
|
||||||
BuiltinConcepts.INVALID_RETURN_VALUE,
|
BuiltinConcepts.INVALID_RETURN_VALUE,
|
||||||
BuiltinConcepts.ALREADY_DEFINED,
|
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
|
||||||
|
BuiltinConcepts.PROPERTY_ALREADY_DEFINED,
|
||||||
BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||||
BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
||||||
BuiltinConcepts.NOT_A_SET,
|
BuiltinConcepts.NOT_A_SET,
|
||||||
@@ -479,6 +482,33 @@ class ConceptAlreadyInSet(Concept):
|
|||||||
return self.get_value("concept_set")
|
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):
|
class ConditionFailed(Concept):
|
||||||
def __init__(self, condition=None, concept=None, prop=None):
|
def __init__(self, condition=None, concept=None, prop=None):
|
||||||
super().__init__(BuiltinConcepts.CONDITION_FAILED,
|
super().__init__(BuiltinConcepts.CONDITION_FAILED,
|
||||||
|
|||||||
+16
-16
@@ -565,7 +565,7 @@ class Sheerka(Concept):
|
|||||||
return c
|
return c
|
||||||
|
|
||||||
metadata = [(index_name, key), ("id", concept_id)] if concept_id else (index_name, key)
|
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):
|
def resolve(self, concept):
|
||||||
"""
|
"""
|
||||||
@@ -827,20 +827,20 @@ class Sheerka(Concept):
|
|||||||
|
|
||||||
return self.parsers_prefix + name
|
return self.parsers_prefix + name
|
||||||
|
|
||||||
def concepts(self):
|
# def concepts(self):
|
||||||
"""
|
# """
|
||||||
List of all known concepts (look up in sdp)
|
# List of all known concepts (look up in sdp)
|
||||||
:return:
|
# :return:
|
||||||
"""
|
# """
|
||||||
res = []
|
# res = []
|
||||||
lst = self.sdp.list(self.CONCEPTS_BY_ID_ENTRY)
|
# lst = self.sdp.list(self.CONCEPTS_BY_ID_ENTRY)
|
||||||
for item in lst:
|
# for item in lst:
|
||||||
if isinstance(item, list):
|
# if isinstance(item, list):
|
||||||
res.extend(item)
|
# res.extend(item)
|
||||||
else:
|
# else:
|
||||||
res.append(item)
|
# res.append(item)
|
||||||
|
#
|
||||||
return sorted(res, key=lambda i: int(i.id))
|
# return sorted(res, key=lambda i: int(i.id))
|
||||||
|
|
||||||
def get_last_execution(self):
|
def get_last_execution(self):
|
||||||
return self._last_execution
|
return self._last_execution
|
||||||
@@ -896,7 +896,7 @@ class Sheerka(Concept):
|
|||||||
return a.key == b_key
|
return a.key == b_key
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_unknown(metadata):
|
def get_unknown(metadata):
|
||||||
"""
|
"""
|
||||||
Returns the concept 'UnknownConcept' for a requested id or key
|
Returns the concept 'UnknownConcept' for a requested id or key
|
||||||
Note that I don't call the new() method to prevent cyclic call
|
Note that I don't call the new() method to prevent cyclic call
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ class SheerkaComparisonManager(BaseService):
|
|||||||
co.b == comparison_obj.b and \
|
co.b == comparison_obj.b and \
|
||||||
co.op == comparison_obj.op and \
|
co.op == comparison_obj.op and \
|
||||||
co.context == comparison_obj.context:
|
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)
|
new.append(comparison_obj)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -44,7 +44,7 @@ class SheerkaCreateNewConcept(BaseService):
|
|||||||
return sheerka.ret(
|
return sheerka.ret(
|
||||||
self.NAME,
|
self.NAME,
|
||||||
False,
|
False,
|
||||||
sheerka.new(BuiltinConcepts.ALREADY_DEFINED, body=concept),
|
sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept),
|
||||||
error.args[0])
|
error.args[0])
|
||||||
|
|
||||||
# set id before saving in db
|
# set id before saving in db
|
||||||
|
|||||||
@@ -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])
|
||||||
@@ -44,7 +44,7 @@ class SheerkaModifyConcept(BaseService):
|
|||||||
return self.sheerka.ret(
|
return self.sheerka.ret(
|
||||||
self.NAME, False,
|
self.NAME, False,
|
||||||
self.sheerka.new(
|
self.sheerka.new(
|
||||||
BuiltinConcepts.ALREADY_DEFINED,
|
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
|
||||||
body=concept))
|
body=concept))
|
||||||
|
|
||||||
self.sheerka.cache_manager.update_concept(old_version, concept)
|
self.sheerka.cache_manager.update_concept(old_version, concept)
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ class SheerkaSetsManager(BaseService):
|
|||||||
for c in a.metadata.props[BuiltinConcepts.ISA]:
|
for c in a.metadata.props[BuiltinConcepts.ISA]:
|
||||||
if c == b:
|
if c == b:
|
||||||
return True
|
return True
|
||||||
if self.isa(c, b):
|
if self.isa(self.sheerka.get_by_id(c.id), b):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -334,4 +334,4 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
|||||||
res = self.execution_definition(context, service, concepts_map, definition)
|
res = self.execution_definition(context, service, concepts_map, definition)
|
||||||
|
|
||||||
assert not res.status
|
assert not res.status
|
||||||
assert sheerka.isinstance(res.body, BuiltinConcepts.ALREADY_DEFINED)
|
assert sheerka.isinstance(res.body, BuiltinConcepts.CONCEPT_ALREADY_DEFINED)
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -99,7 +99,7 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
|
|||||||
res = sheerka.create_new_concept(self.get_context(sheerka), concept)
|
res = sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||||
|
|
||||||
assert not res.status
|
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
|
assert res.value.body == concept
|
||||||
|
|
||||||
def test_i_can_get_a_newly_created_concept(self):
|
def test_i_can_get_a_newly_created_concept(self):
|
||||||
@@ -260,7 +260,7 @@ class TestSheerkaCreateNewConceptFileBased(TestUsingFileBasedSheerka):
|
|||||||
res = sheerka.create_new_concept(context, concept)
|
res = sheerka.create_new_concept(context, concept)
|
||||||
|
|
||||||
assert not res.status
|
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
|
assert res.value.body == concept
|
||||||
|
|
||||||
def test_new_entry_does_not_override_the_previous_ones(self):
|
def test_new_entry_does_not_override_the_previous_ones(self):
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -73,7 +73,7 @@ class TestSheerkaModifyConcept(TestUsingMemoryBasedSheerka):
|
|||||||
res = sheerka.modify_concept(context, foo)
|
res = sheerka.modify_concept(context, foo)
|
||||||
|
|
||||||
assert not res.status
|
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):
|
def test_i_can_modify_a_concept_that_is_in_a_list(self):
|
||||||
sheerka, context, foo1, foo2 = self.init_concepts(
|
sheerka, context, foo1, foo2 = self.init_concepts(
|
||||||
|
|||||||
@@ -241,12 +241,12 @@ class TestSheerkaSetsManager(TestUsingMemoryBasedSheerka):
|
|||||||
Concept("baz"),
|
Concept("baz"),
|
||||||
)
|
)
|
||||||
|
|
||||||
sheerka.set_isa(context, foo, bar)
|
sheerka.set_isa(context, sheerka.new("foo"), bar)
|
||||||
sheerka.set_isa(context, bar, baz)
|
sheerka.set_isa(context, sheerka.new("bar"), baz)
|
||||||
|
|
||||||
assert sheerka.isa(foo, bar)
|
assert sheerka.isa(sheerka.new("foo"), bar)
|
||||||
assert sheerka.isa(bar, baz)
|
assert sheerka.isa(sheerka.new("bar"), baz)
|
||||||
assert sheerka.isa(foo, baz)
|
assert sheerka.isa(sheerka.new("foo"), baz)
|
||||||
|
|
||||||
def test_i_cannot_manage_isa_transitivity_when_using_body(self):
|
def test_i_cannot_manage_isa_transitivity_when_using_body(self):
|
||||||
sheerka, context, one, another_one, number = self.init_concepts(
|
sheerka, context, one, another_one, number = self.init_concepts(
|
||||||
@@ -255,9 +255,9 @@ class TestSheerkaSetsManager(TestUsingMemoryBasedSheerka):
|
|||||||
"number"
|
"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
|
assert not sheerka.isa(another_one, number) # Correct this misbehaviour when BuiltinConcepts.IS is implemented
|
||||||
|
|
||||||
def test_concept_expression_recurse_id_is_updated(self):
|
def test_concept_expression_recurse_id_is_updated(self):
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ as:
|
|||||||
|
|
||||||
assert len(res) == 1
|
assert len(res) == 1
|
||||||
assert not res[0].status
|
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", [
|
@pytest.mark.parametrize("text", [
|
||||||
"",
|
"",
|
||||||
|
|||||||
Reference in New Issue
Block a user