Introduced ConceptsAlgebra
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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(
|
||||
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
|
||||
|
||||
@@ -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(
|
||||
self.NAME, False,
|
||||
self.sheerka.new(
|
||||
BuiltinConcepts.ALREADY_DEFINED,
|
||||
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
|
||||
body=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]:
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user