fuzzy-dl-owl2 1.0.2__py3-none-any.whl → 1.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. fuzzy_dl_owl2/fuzzydl/concept/approximation_concept.py +35 -10
  2. fuzzy_dl_owl2/fuzzydl/concept/concrete/fuzzy_number/triangular_fuzzy_number.py +2 -1
  3. fuzzy_dl_owl2/fuzzydl/concept/concrete/trapezoidal_concrete_concept.py +1 -0
  4. fuzzy_dl_owl2/fuzzydl/concept/implies_concept.py +14 -11
  5. fuzzy_dl_owl2/fuzzydl/concept/operator_concept.py +10 -6
  6. fuzzy_dl_owl2/fuzzydl/concept/value_concept.py +1 -1
  7. fuzzy_dl_owl2/fuzzydl/concrete_feature.py +7 -1
  8. fuzzy_dl_owl2/fuzzydl/feature_function.py +16 -2
  9. fuzzy_dl_owl2/fuzzydl/fuzzydl_to_owl2.py +1 -1
  10. fuzzy_dl_owl2/fuzzydl/knowledge_base.py +494 -358
  11. fuzzy_dl_owl2/fuzzydl/milp/expression.py +4 -2
  12. fuzzy_dl_owl2/fuzzydl/milp/milp_helper.py +3 -2
  13. fuzzy_dl_owl2/fuzzydl/milp/variable.py +3 -0
  14. fuzzy_dl_owl2/fuzzydl/modifier/triangular_modifier.py +1 -1
  15. fuzzy_dl_owl2/fuzzydl/parser/dl_parser.py +1465 -1210
  16. fuzzy_dl_owl2/fuzzydl/query/defuzzify/mom_defuzzify_query.py +14 -7
  17. fuzzy_dl_owl2/fuzzydl/query/min/min_subsumes_query.py +2 -2
  18. fuzzy_dl_owl2/fuzzydl/util/config_reader.py +0 -6
  19. fuzzy_dl_owl2/fuzzydl/util/constants.py +10 -9
  20. fuzzy_dl_owl2/fuzzydl/util/utils.py +48 -7
  21. fuzzy_dl_owl2/fuzzyowl2/fuzzyowl2.py +1 -1
  22. {fuzzy_dl_owl2-1.0.2.dist-info → fuzzy_dl_owl2-1.0.3.dist-info}/METADATA +9 -7
  23. {fuzzy_dl_owl2-1.0.2.dist-info → fuzzy_dl_owl2-1.0.3.dist-info}/RECORD +25 -25
  24. {fuzzy_dl_owl2-1.0.2.dist-info → fuzzy_dl_owl2-1.0.3.dist-info}/LICENSE +0 -0
  25. {fuzzy_dl_owl2-1.0.2.dist-info → fuzzy_dl_owl2-1.0.3.dist-info}/WHEEL +0 -0
@@ -10,79 +10,84 @@ from sortedcontainers import SortedSet
10
10
 
11
11
  from fuzzy_dl_owl2.fuzzydl.assertion.assertion import Assertion
12
12
  from fuzzy_dl_owl2.fuzzydl.concept.all_some_concept import AllSomeConcept
13
- from fuzzy_dl_owl2.fuzzydl.concept.approximation_concept import \
14
- ApproximationConcept
13
+ from fuzzy_dl_owl2.fuzzydl.concept.approximation_concept import ApproximationConcept
15
14
  from fuzzy_dl_owl2.fuzzydl.concept.atomic_concept import AtomicConcept
16
15
  from fuzzy_dl_owl2.fuzzydl.concept.choquet_integral import ChoquetIntegral
17
16
  from fuzzy_dl_owl2.fuzzydl.concept.concept import Concept
18
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.crisp_concrete_concept import \
19
- CrispConcreteConcept
20
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.fuzzy_concrete_concept import \
21
- FuzzyConcreteConcept
22
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.fuzzy_number.triangular_fuzzy_number import \
23
- TriangularFuzzyNumber
24
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.left_concrete_concept import \
25
- LeftConcreteConcept
26
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.linear_concrete_concept import \
27
- LinearConcreteConcept
28
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.modified_concrete_concept import \
29
- ModifiedConcreteConcept
30
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.right_concrete_concept import \
31
- RightConcreteConcept
32
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.trapezoidal_concrete_concept import \
33
- TrapezoidalConcreteConcept
34
- from fuzzy_dl_owl2.fuzzydl.concept.concrete.triangular_concrete_concept import \
35
- TriangularConcreteConcept
36
- from fuzzy_dl_owl2.fuzzydl.concept.ext_threshold_concept import \
37
- ExtThresholdConcept
17
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.crisp_concrete_concept import (
18
+ CrispConcreteConcept,
19
+ )
20
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.fuzzy_concrete_concept import (
21
+ FuzzyConcreteConcept,
22
+ )
23
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.fuzzy_number.triangular_fuzzy_number import (
24
+ TriangularFuzzyNumber,
25
+ )
26
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.left_concrete_concept import (
27
+ LeftConcreteConcept,
28
+ )
29
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.linear_concrete_concept import (
30
+ LinearConcreteConcept,
31
+ )
32
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.modified_concrete_concept import (
33
+ ModifiedConcreteConcept,
34
+ )
35
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.right_concrete_concept import (
36
+ RightConcreteConcept,
37
+ )
38
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.trapezoidal_concrete_concept import (
39
+ TrapezoidalConcreteConcept,
40
+ )
41
+ from fuzzy_dl_owl2.fuzzydl.concept.concrete.triangular_concrete_concept import (
42
+ TriangularConcreteConcept,
43
+ )
44
+ from fuzzy_dl_owl2.fuzzydl.concept.ext_threshold_concept import ExtThresholdConcept
38
45
  from fuzzy_dl_owl2.fuzzydl.concept.has_value_concept import HasValueConcept
39
46
  from fuzzy_dl_owl2.fuzzydl.concept.implies_concept import ImpliesConcept
40
- from fuzzy_dl_owl2.fuzzydl.concept.interface.has_concepts_interface import \
41
- HasConceptsInterface
42
- from fuzzy_dl_owl2.fuzzydl.concept.interface.has_role_interface import \
43
- HasRoleInterface
44
- from fuzzy_dl_owl2.fuzzydl.concept.interface.has_value_interface import \
45
- HasValueInterface
46
- from fuzzy_dl_owl2.fuzzydl.concept.modified.modified_concept import \
47
- ModifiedConcept
48
- from fuzzy_dl_owl2.fuzzydl.concept.modified.triangularly_modified_concept import \
49
- TriangularlyModifiedConcept
47
+ from fuzzy_dl_owl2.fuzzydl.concept.interface.has_concepts_interface import (
48
+ HasConceptsInterface,
49
+ )
50
+ from fuzzy_dl_owl2.fuzzydl.concept.interface.has_role_interface import HasRoleInterface
51
+ from fuzzy_dl_owl2.fuzzydl.concept.interface.has_value_interface import (
52
+ HasValueInterface,
53
+ )
54
+ from fuzzy_dl_owl2.fuzzydl.concept.modified.modified_concept import ModifiedConcept
55
+ from fuzzy_dl_owl2.fuzzydl.concept.modified.triangularly_modified_concept import (
56
+ TriangularlyModifiedConcept,
57
+ )
50
58
  from fuzzy_dl_owl2.fuzzydl.concept.negated_nominal import NegatedNominal
51
59
  from fuzzy_dl_owl2.fuzzydl.concept.operator_concept import OperatorConcept
52
60
  from fuzzy_dl_owl2.fuzzydl.concept.owa_concept import OwaConcept
53
61
  from fuzzy_dl_owl2.fuzzydl.concept.qowa_concept import QowaConcept
54
- from fuzzy_dl_owl2.fuzzydl.concept.quasi_sugeno_integral import \
55
- QsugenoIntegral
62
+ from fuzzy_dl_owl2.fuzzydl.concept.quasi_sugeno_integral import QsugenoIntegral
56
63
  from fuzzy_dl_owl2.fuzzydl.concept.self_concept import SelfConcept
57
64
  from fuzzy_dl_owl2.fuzzydl.concept.sugeno_integral import SugenoIntegral
58
65
  from fuzzy_dl_owl2.fuzzydl.concept.threshold_concept import ThresholdConcept
59
66
  from fuzzy_dl_owl2.fuzzydl.concept.truth_concept import TruthConcept
60
67
  from fuzzy_dl_owl2.fuzzydl.concept.value_concept import ValueConcept
61
68
  from fuzzy_dl_owl2.fuzzydl.concept.weighted_concept import WeightedConcept
62
- from fuzzy_dl_owl2.fuzzydl.concept.weighted_max_concept import \
63
- WeightedMaxConcept
64
- from fuzzy_dl_owl2.fuzzydl.concept.weighted_min_concept import \
65
- WeightedMinConcept
66
- from fuzzy_dl_owl2.fuzzydl.concept.weighted_sum_concept import \
67
- WeightedSumConcept
68
- from fuzzy_dl_owl2.fuzzydl.concept.weighted_sum_zero_concept import \
69
- WeightedSumZeroConcept
69
+ from fuzzy_dl_owl2.fuzzydl.concept.weighted_max_concept import WeightedMaxConcept
70
+ from fuzzy_dl_owl2.fuzzydl.concept.weighted_min_concept import WeightedMinConcept
71
+ from fuzzy_dl_owl2.fuzzydl.concept.weighted_sum_concept import WeightedSumConcept
72
+ from fuzzy_dl_owl2.fuzzydl.concept.weighted_sum_zero_concept import (
73
+ WeightedSumZeroConcept,
74
+ )
70
75
  from fuzzy_dl_owl2.fuzzydl.concept_equivalence import ConceptEquivalence
71
76
  from fuzzy_dl_owl2.fuzzydl.concrete_feature import ConcreteFeature
72
77
  from fuzzy_dl_owl2.fuzzydl.degree.degree import Degree
73
78
  from fuzzy_dl_owl2.fuzzydl.degree.degree_expression import DegreeExpression
74
79
  from fuzzy_dl_owl2.fuzzydl.degree.degree_numeric import DegreeNumeric
75
80
  from fuzzy_dl_owl2.fuzzydl.degree.degree_variable import DegreeVariable
76
- from fuzzy_dl_owl2.fuzzydl.exception.inconsistent_ontology_exception import \
77
- InconsistentOntologyException
81
+ from fuzzy_dl_owl2.fuzzydl.exception.inconsistent_ontology_exception import (
82
+ InconsistentOntologyException,
83
+ )
78
84
  from fuzzy_dl_owl2.fuzzydl.feature_function import FeatureFunction
79
- from fuzzy_dl_owl2.fuzzydl.general_concept_inclusion import \
80
- GeneralConceptInclusion
81
- from fuzzy_dl_owl2.fuzzydl.individual.created_individual import \
82
- CreatedIndividual
85
+ from fuzzy_dl_owl2.fuzzydl.general_concept_inclusion import GeneralConceptInclusion
86
+ from fuzzy_dl_owl2.fuzzydl.individual.created_individual import CreatedIndividual
83
87
  from fuzzy_dl_owl2.fuzzydl.individual.individual import Individual
84
- from fuzzy_dl_owl2.fuzzydl.individual.representative_individual import \
85
- RepresentativeIndividual
88
+ from fuzzy_dl_owl2.fuzzydl.individual.representative_individual import (
89
+ RepresentativeIndividual,
90
+ )
86
91
  from fuzzy_dl_owl2.fuzzydl.milp.expression import Expression
87
92
  from fuzzy_dl_owl2.fuzzydl.milp.milp_helper import MILPHelper
88
93
  from fuzzy_dl_owl2.fuzzydl.milp.solution import Solution
@@ -90,28 +95,38 @@ from fuzzy_dl_owl2.fuzzydl.milp.term import Term
90
95
  from fuzzy_dl_owl2.fuzzydl.milp.variable import Variable
91
96
  from fuzzy_dl_owl2.fuzzydl.modifier.linear_modifier import LinearModifier
92
97
  from fuzzy_dl_owl2.fuzzydl.modifier.modifier import Modifier
93
- from fuzzy_dl_owl2.fuzzydl.modifier.triangular_modifier import \
94
- TriangularModifier
95
- from fuzzy_dl_owl2.fuzzydl.primitive_concept_definition import \
96
- PrimitiveConceptDefinition
98
+ from fuzzy_dl_owl2.fuzzydl.modifier.triangular_modifier import TriangularModifier
99
+ from fuzzy_dl_owl2.fuzzydl.primitive_concept_definition import (
100
+ PrimitiveConceptDefinition,
101
+ )
97
102
  from fuzzy_dl_owl2.fuzzydl.relation import Relation
98
- from fuzzy_dl_owl2.fuzzydl.restriction.has_value_restriction import \
99
- HasValueRestriction
103
+ from fuzzy_dl_owl2.fuzzydl.restriction.has_value_restriction import HasValueRestriction
100
104
  from fuzzy_dl_owl2.fuzzydl.restriction.restriction import Restriction
101
105
  from fuzzy_dl_owl2.fuzzydl.util import constants
102
106
  from fuzzy_dl_owl2.fuzzydl.util.config_reader import ConfigReader
103
107
  from fuzzy_dl_owl2.fuzzydl.util.constants import (
104
- BlockingDynamicType, ConceptType, ConcreteFeatureType,
105
- CreatedIndividualBlockingType, FeatureFunctionType, FuzzyLogic,
106
- InequalityType, KnowledgeBaseRules, LogicOperatorType,
107
- RepresentativeIndividualType, RestrictionType, VariableType)
108
+ BlockingDynamicType,
109
+ ConceptType,
110
+ ConcreteFeatureType,
111
+ CreatedIndividualBlockingType,
112
+ FeatureFunctionType,
113
+ FuzzyLogic,
114
+ InequalityType,
115
+ KnowledgeBaseRules,
116
+ LogicOperatorType,
117
+ RepresentativeIndividualType,
118
+ RestrictionType,
119
+ VariableType,
120
+ )
108
121
  from fuzzy_dl_owl2.fuzzydl.util.util import Util
122
+ from fuzzy_dl_owl2.fuzzydl.util.utils import class_debugging
109
123
 
110
124
 
125
+ @class_debugging()
111
126
  class KnowledgeBase:
112
- VERSION: float = 2.0
113
127
 
114
128
  def __init__(self) -> None:
129
+ Variable.VARIABLE_NUMBER = 0
115
130
  self.language: str = ""
116
131
 
117
132
  self.milp: MILPHelper = MILPHelper()
@@ -342,7 +357,9 @@ class KnowledgeBase:
342
357
  k: [r.clone() for r in v] for k, v in self.temp_relations_list.items()
343
358
  }
344
359
  kb.t_G = [gci.clone() for gci in self.t_G]
345
- kb.t_inclusions = {k: set([pcd.clone() for pcd in v]) for k, v in self.t_inclusions.items()}
360
+ kb.t_inclusions = {
361
+ k: set([pcd.clone() for pcd in v]) for k, v in self.t_inclusions.items()
362
+ }
346
363
  kb.transitive_roles = copy.deepcopy(self.transitive_roles)
347
364
  kb.t_synonyms = copy.deepcopy(self.t_synonyms)
348
365
  return kb
@@ -746,9 +763,9 @@ class KnowledgeBase:
746
763
  return rel
747
764
 
748
765
  def define_synonym(self, concept_name_1: str, concept_name_2: str) -> None:
749
- self.t_synonyms[concept_name_1] = self.t_synonyms.get(concept_name_1, set()) | set(
750
- [concept_name_2]
751
- )
766
+ self.t_synonyms[concept_name_1] = self.t_synonyms.get(
767
+ concept_name_1, set()
768
+ ) | set([concept_name_2])
752
769
  self.get_concept(concept_name_1)
753
770
 
754
771
  def define_synonyms(self, concept_name_1: str, concept_name_2: str) -> None:
@@ -1262,7 +1279,9 @@ class KnowledgeBase:
1262
1279
 
1263
1280
  def add_simple_inverse_roles(self, role: str, inv_role: str) -> None:
1264
1281
  self.inverse_roles[role] = self.inverse_roles.get(role, set()) | set([inv_role])
1265
- self.inverse_roles[inv_role] = self.inverse_roles.get(inv_role, set()) | set([role])
1282
+ self.inverse_roles[inv_role] = self.inverse_roles.get(inv_role, set()) | set(
1283
+ [role]
1284
+ )
1266
1285
  if role in self.inverse_functional_roles:
1267
1286
  self.functional_roles.add(inv_role)
1268
1287
  if inv_role in self.inverse_functional_roles:
@@ -1746,7 +1765,7 @@ class KnowledgeBase:
1746
1765
  Util.debug(f"{constants.SEPARATOR}GCI completed{constants.SEPARATOR}")
1747
1766
 
1748
1767
  def solve_reflexive_role(self, role: str) -> None:
1749
- for ind in self.individuals:
1768
+ for ind in self.individuals.values():
1750
1769
  self.add_relation(ind, role, ind, DegreeNumeric.get_degree(1.0))
1751
1770
 
1752
1771
  @typing.overload
@@ -1789,7 +1808,7 @@ class KnowledgeBase:
1789
1808
  def __get_correct_version_of_individual_1(self, ass: Assertion) -> None:
1790
1809
  ind: Individual = ass.get_individual()
1791
1810
  ind2: Individual = self.individuals.get(str(ind))
1792
- if ind == ind2:
1811
+ if id(ind) == id(ind2):
1793
1812
  return
1794
1813
  if ind2 is None:
1795
1814
  ind2 = self.get_individual(str(ind))
@@ -1799,7 +1818,7 @@ class KnowledgeBase:
1799
1818
  def __get_correct_version_of_individual_2(self, rel: Relation) -> None:
1800
1819
  ind: Individual = rel.get_object_individual()
1801
1820
  ind2: Individual = self.individuals.get(str(ind))
1802
- if ind == ind2:
1821
+ if id(ind) == id(ind2):
1803
1822
  return
1804
1823
  if ind2 is None:
1805
1824
  ind2 = self.get_individual(str(ind))
@@ -1880,28 +1899,44 @@ class KnowledgeBase:
1880
1899
  self.individuals[b_name] = a
1881
1900
 
1882
1901
  def merge(self, a: Individual, b: Individual) -> None:
1902
+ """
1903
+ Merges two individuals.
1904
+ """
1905
+
1883
1906
  if isinstance(a, CreatedIndividual) and not isinstance(b, CreatedIndividual):
1907
+ # Swap b and a, so the created individual is merged into the root individual
1884
1908
  a, b = b, a
1885
1909
 
1886
1910
  a_name, b_name = str(a), str(b)
1887
1911
  Util.debug(f"Merging individual {b_name} into {a_name}")
1912
+ # To do: nominal variables needed only if language contains "B"
1913
+
1914
+ # Unique Name Assumption
1888
1915
  if not isinstance(a, CreatedIndividual) and not isinstance(
1889
1916
  b, CreatedIndividual
1890
1917
  ):
1918
+ # xAisA + xBisB <= 1
1891
1919
  x_a_is_a: Variable = self.milp.get_nominal_variable(a_name)
1892
1920
  x_b_is_b: Variable = self.milp.get_nominal_variable(b_name, b_name)
1893
1921
  self.milp.add_new_constraint(
1894
1922
  Expression(-1.0, Term(1.0, x_a_is_a), Term(1.0, x_b_is_b)),
1895
1923
  InequalityType.LESS_THAN,
1896
1924
  )
1925
+ # Add { b } to a
1897
1926
  self.add_labels_with_nodes(b_name, a_name)
1898
1927
 
1928
+ # --------------------------------------------------------------
1929
+ # 1. Move edges leading to b so that they lead to a
1930
+ # --------------------------------------------------------------
1899
1931
  for i in self.individuals.values():
1900
1932
  for array in i.role_relations.values():
1901
1933
  for r in array:
1902
1934
  if r.get_object_individual() == b:
1903
1935
  r.set_object_individual(a)
1904
1936
 
1937
+ # --------------------------------------------------------------------------
1938
+ # 2. Move edges leading from b to a nominal node so that they lead from a
1939
+ # --------------------------------------------------------------------------
1905
1940
  to_remove: set[str] = set()
1906
1941
  for role, b_rels in b.role_relations.items():
1907
1942
  new_rels: list[Relation] = []
@@ -1910,18 +1945,21 @@ class KnowledgeBase:
1910
1945
  if not r.get_object_individual().is_blockable():
1911
1946
  r.set_subject_individual(a)
1912
1947
  a_rels.append(r)
1913
- continue
1914
- new_rels.append(r)
1948
+ else:
1949
+ new_rels.append(r)
1915
1950
 
1916
1951
  a.role_relations[role] = a_rels
1917
1952
  if len(new_rels) == 0:
1918
1953
  to_remove.add(role)
1919
- continue
1920
- b.role_relations[role] = new_rels
1954
+ else:
1955
+ b.role_relations[role] = new_rels
1921
1956
 
1922
1957
  for role in to_remove:
1923
1958
  del b.role_relations[role]
1924
1959
 
1960
+ # -------------------------------------------------------
1961
+ # 3. Concept assertions using b, now use a
1962
+ # -----------------------------------------------------
1925
1963
  for ass in self.assertions:
1926
1964
  if str(ass.get_individual()) == b_name:
1927
1965
  ass.set_individual(a)
@@ -1930,8 +1968,15 @@ class KnowledgeBase:
1930
1968
  if str(ass.get_individual()) == b_name:
1931
1969
  ass.set_individual(a)
1932
1970
 
1971
+ # -----------------------------------------
1972
+ # 4. Variables using b, now use a
1973
+ # -----------------------------------------
1933
1974
  param: bool = isinstance(b, CreatedIndividual)
1934
1975
  self.milp.change_variable_names(b_name, a_name, param)
1976
+
1977
+ # -----------------------------------------
1978
+ # 5. Prune
1979
+ # -----------------------------------------
1935
1980
  b.prune()
1936
1981
 
1937
1982
  def goedel_implies(self, conc1: Concept, conc2: Concept, degree: Degree) -> None:
@@ -2222,9 +2267,11 @@ class KnowledgeBase:
2222
2267
  role = typing.cast(AllSomeConcept, conc2).role
2223
2268
  c_range: Concept = typing.cast(AllSomeConcept, conc2).curr_concept
2224
2269
  else:
2225
- assert isinstance(conc2, HasValueInterface)
2226
- role = conc2.role
2227
- c_range: Concept = NegatedNominal(str(conc2.value))
2270
+ assert isinstance(conc2, OperatorConcept)
2271
+ has_value: Concept = conc2.get_atom()
2272
+ assert isinstance(has_value, HasValueInterface)
2273
+ role = has_value.role
2274
+ c_range: Concept = NegatedNominal(str(has_value.value))
2228
2275
  self.role_range(role, c_range)
2229
2276
  self.remove_C_is_a_X(key, tau, atomic)
2230
2277
  Util.debug(f"Absorbed: range {role}, {c_range}")
@@ -2240,8 +2287,14 @@ class KnowledgeBase:
2240
2287
  if (
2241
2288
  type_c2 == ConceptType.ALL or OperatorConcept.is_not_has_value(conc2)
2242
2289
  ) and degree_is_one:
2243
- assert isinstance(conc2, HasRoleInterface)
2244
- role: str = conc2.role
2290
+ if OperatorConcept.is_not_has_value(conc2):
2291
+ atom: Concept = typing.cast(OperatorConcept, conc2).get_atom()
2292
+ assert isinstance(atom, HasRoleInterface)
2293
+ role: str = atom.role
2294
+ else:
2295
+ assert isinstance(conc2, HasRoleInterface)
2296
+ role: str = conc2.role
2297
+
2245
2298
  if self.is_crisp_role(role) and (
2246
2299
  implication_type != LogicOperatorType.KLEENE_DIENES
2247
2300
  or constants.KNOWLEDGE_BASE_SEMANTICS == FuzzyLogic.CLASSICAL
@@ -2266,11 +2319,15 @@ class KnowledgeBase:
2266
2319
  -conc1,
2267
2320
  )
2268
2321
  else:
2269
- assert isinstance(conc2, HasValueInterface)
2270
2322
  g_imp_concept: Concept = ImpliesConcept.goedel_implies(
2271
2323
  HasValueConcept(
2272
2324
  role,
2273
- str(conc2.value),
2325
+ str(
2326
+ typing.cast(
2327
+ HasValueConcept,
2328
+ typing.cast(OperatorConcept, conc2).get_atom(),
2329
+ ).value
2330
+ ),
2274
2331
  ),
2275
2332
  -conc1,
2276
2333
  )
@@ -2848,7 +2905,7 @@ class KnowledgeBase:
2848
2905
  Util.debug("No optimization: DOUBLE_BLOCKING + dynamicblocking")
2849
2906
  return
2850
2907
  if len(self.inverse_roles) == 0 or len(self.functional_roles) == 0:
2851
- if len(self.t_G) == 0 or self.is_tbox_acyclic():
2908
+ if len(self.t_G) == 0 and self.is_tbox_acyclic():
2852
2909
  self.blocking_type = BlockingDynamicType.NO_BLOCKING
2853
2910
  Util.debug("NO_BLOCKING")
2854
2911
  else:
@@ -2945,6 +3002,10 @@ class KnowledgeBase:
2945
3002
  def __restrict_range_2(
2946
3003
  self, x_b: Variable, x_f: Variable, k1: float, k2: float
2947
3004
  ) -> None:
3005
+ """
3006
+ Restricts the range of a variable to [k1, k2] if x_f not zero
3007
+ """
3008
+ # x_b \geq k1
2948
3009
  self.milp.add_new_constraint(
2949
3010
  Expression(
2950
3011
  constants.MAXVAL,
@@ -2954,6 +3015,7 @@ class KnowledgeBase:
2954
3015
  ),
2955
3016
  InequalityType.GREATER_THAN,
2956
3017
  )
3018
+ # x_b \leq k2
2957
3019
  self.milp.add_new_constraint(
2958
3020
  Expression(
2959
3021
  -constants.MAXVAL,
@@ -3048,8 +3110,6 @@ class KnowledgeBase:
3048
3110
  def solve_kb(self) -> None:
3049
3111
  if constants.KNOWLEDGE_BASE_SEMANTICS is None:
3050
3112
  self.set_logic(FuzzyLogic.LUKASIEWICZ)
3051
- if ConfigReader.SHOW_VERSION:
3052
- Util.info(f"Version: {self.get_version()}")
3053
3113
  self.compute_language()
3054
3114
  self.convert_strings_into_integers()
3055
3115
  self.solve_inverse_roles()
@@ -3136,188 +3196,203 @@ class KnowledgeBase:
3136
3196
  Util.debug(
3137
3197
  f"{constants.SEPARATOR}Assertion completed{constants.SEPARATOR}"
3138
3198
  )
3199
+ continue
3200
+
3201
+ # Use right version of the individual (needed when we clone the KB or merge individuals)
3202
+ self.get_correct_version_of_individual(ass)
3203
+ if ass.get_individual().is_blockable():
3204
+ Util.debug(
3205
+ f"Direct Blocking status {typing.cast(CreatedIndividual, ass.get_individual()).directly_blocked}"
3206
+ )
3207
+ Util.debug(
3208
+ f"Indirect Blocking status {typing.cast(CreatedIndividual, ass.get_individual()).indirectly_blocked}"
3209
+ )
3210
+
3211
+ # If the individual is indirectly blocked we skip the assertion
3212
+ if (
3213
+ ass.get_individual().is_blockable()
3214
+ and CreatedIndividualHandler.is_indirectly_blocked(
3215
+ typing.cast(CreatedIndividual, ass.get_individual()), self
3216
+ )
3217
+ ):
3218
+ name: str = str(ass.get_individual())
3219
+ Util.debug(
3220
+ "Skipping assertion (it has an indirectly blocked individual)"
3221
+ )
3222
+ self.blocked_assertions[name] = self.blocked_assertions.get(
3223
+ name, []
3224
+ ) + [ass]
3225
+ continue
3226
+
3227
+ # Add xAss >= lowerBound
3228
+ self.milp.add_new_constraint(ass)
3229
+ if self.is_assertion_processed(ass):
3230
+ Util.debug(
3231
+ f"Assertion (without the degree): {ass} already processed."
3232
+ )
3233
+ continue
3234
+
3235
+ ind: Individual = ass.get_individual()
3236
+ ci: Concept = ass.get_concept()
3237
+ self.add_negated_equations(ind, ci)
3238
+ c_type: ConceptType = ass.get_type()
3239
+
3240
+ # Apply reasoning rule according to the type of the assertion
3241
+ if c_type == ConceptType.ATOMIC:
3242
+ self.rule_atomic(ass)
3243
+ elif ci.is_complemented_atomic():
3244
+ self.rule_complemented_atomic(ass)
3245
+ elif c_type == ConceptType.AND:
3246
+ self.rule_and(ass)
3247
+ elif c_type == ConceptType.OR:
3248
+ self.rule_or(ass)
3249
+ elif c_type in (ConceptType.SOME, ConceptType.HAS_VALUE):
3250
+ self.exist_assertions.append(ass)
3251
+ continue
3252
+ elif c_type == ConceptType.ALL:
3253
+ self.rule_all(ass)
3254
+ elif OperatorConcept.is_not_has_value(ci):
3255
+ self.rule_complemented_has_value(ass)
3256
+ elif c_type == ConceptType.CONCRETE:
3257
+ self.rule_concrete(ass)
3258
+ elif OperatorConcept.is_not_concrete(ci):
3259
+ self.rule_complemented_concrete(ass)
3260
+ elif c_type == ConceptType.FUZZY_NUMBER:
3261
+ self.rule_fuzzy_number(ass)
3262
+ elif OperatorConcept.is_not_fuzzy_number(ci):
3263
+ self.rule_complemented_fuzzy_number(ass)
3264
+ elif c_type == ConceptType.MODIFIED:
3265
+ self.rule_modified(ass)
3266
+ elif OperatorConcept.is_not_modified(ci):
3267
+ self.rule_complemented_modified(ass)
3268
+ elif c_type == ConceptType.TOP:
3269
+ self.rule_top(ass)
3270
+ elif c_type == ConceptType.BOTTOM:
3271
+ self.rule_bottom(ass)
3272
+ elif c_type in (
3273
+ ConceptType.AT_MOST_VALUE,
3274
+ ConceptType.AT_LEAST_VALUE,
3275
+ ConceptType.EXACT_VALUE,
3276
+ ):
3277
+ self.positive_concrete_value_assertions.append(ass)
3278
+ elif (
3279
+ OperatorConcept.is_not_at_least_value(ci)
3280
+ or OperatorConcept.is_not_at_most_value(ci)
3281
+ or OperatorConcept.is_not_exact_value(ci)
3282
+ ):
3283
+ self.add_negated_datatype_restriction(ass)
3284
+ elif c_type == ConceptType.SELF:
3285
+ self.rule_self(ass)
3286
+ elif OperatorConcept.is_not_self(ci):
3287
+ self.rule_complemented_self(ass)
3288
+ elif c_type == ConceptType.UPPER_APPROX:
3289
+ self.rule_upper_approximation(ass)
3290
+ elif c_type == ConceptType.TIGHT_UPPER_APPROX:
3291
+ self.rule_tight_upper_approximation(ass)
3292
+ elif c_type == ConceptType.LOOSE_UPPER_APPROX:
3293
+ self.rule_loose_upper_approximation(ass)
3294
+ elif c_type == ConceptType.LOWER_APPROX:
3295
+ self.rule_lower_approximation(ass)
3296
+ elif c_type == ConceptType.TIGHT_LOWER_APPROX:
3297
+ self.rule_tight_lower_approximation(ass)
3298
+ elif c_type == ConceptType.LOOSE_LOWER_APPROX:
3299
+ self.rule_loose_lower_approximation(ass)
3300
+ elif c_type == ConceptType.GOEDEL_AND:
3301
+ self.rule_goedel_and(ass)
3302
+ elif c_type == ConceptType.LUKASIEWICZ_AND:
3303
+ self.rule_lukasiewicz_and(ass)
3304
+ elif c_type == ConceptType.GOEDEL_OR:
3305
+ self.rule_goedel_or(ass)
3306
+ elif c_type == ConceptType.LUKASIEWICZ_OR:
3307
+ self.rule_lukasiewicz_or(ass)
3308
+ elif c_type == ConceptType.GOEDEL_IMPLIES:
3309
+ self.rule_goedel_implication(ass)
3310
+ elif OperatorConcept.is_not_goedel_implies(ci):
3311
+ self.rule_complemented_goedel_implication(ass)
3312
+ elif c_type == ConceptType.ZADEH_IMPLIES:
3313
+ self.rule_zadeh_implication(ass)
3314
+ elif OperatorConcept.is_not_zadeh_implies(ci):
3315
+ self.rule_complemented_zadeh_implication(ass)
3316
+ elif c_type == ConceptType.W_SUM:
3317
+ self.rule_weighted_sum(ass)
3318
+ elif OperatorConcept.is_not_weighted_sum(ci):
3319
+ self.rule_complemented_weighted_sum(ass)
3320
+ elif c_type == ConceptType.W_SUM_ZERO:
3321
+ self.rule_weighted_sum_zero(ass)
3322
+ elif OperatorConcept.is_not_weighted_sum_zero(ci):
3323
+ self.rule_complemented_weighted_sum_zero(ass)
3324
+ elif c_type == ConceptType.WEIGHTED:
3325
+ self.rule_weighted_concept(ass)
3326
+ elif OperatorConcept.is_not_weighted(ci):
3327
+ self.rule_complemented_weighted_concept(ass)
3328
+ elif c_type == ConceptType.POS_THRESHOLD:
3329
+ self.rule_positive_threshold(ass)
3330
+ elif OperatorConcept.is_not_pos_threshold(ci):
3331
+ self.rule_complemented_positive_threshold(ass)
3332
+ elif c_type == ConceptType.NEG_THRESHOLD:
3333
+ self.rule_negative_threshold(ass)
3334
+ elif OperatorConcept.is_not_neg_threshold(ci):
3335
+ self.rule_complemented_negative_threshold(ass)
3336
+ elif c_type == ConceptType.EXT_POS_THRESHOLD:
3337
+ self.rule_extended_positive_threshold(ass)
3338
+ elif OperatorConcept.is_not_ext_pos_threshold(ci):
3339
+ self.rule_complemented_extended_positive_threshold(ass)
3340
+ elif c_type == ConceptType.EXT_NEG_THRESHOLD:
3341
+ self.rule_extended_negative_threshold(ass)
3342
+ elif OperatorConcept.is_not_ext_neg_threshold(ci):
3343
+ self.rule_complemented_extended_negative_threshold(ass)
3344
+ elif c_type == ConceptType.OWA:
3345
+ self.rule_owa(ass)
3346
+ elif OperatorConcept.is_not_owa(ci):
3347
+ self.rule_complemented_owa(ass)
3348
+ elif c_type == ConceptType.QUANTIFIED_OWA:
3349
+ self.rule_quantified_owa(ass)
3350
+ elif OperatorConcept.is_not_qowa(ci):
3351
+ self.rule_complemented_quantified_owa(ass)
3352
+ elif c_type == ConceptType.CHOQUET_INTEGRAL:
3353
+ self.rule_choquet(ass)
3354
+ elif OperatorConcept.is_not_choquet(ci):
3355
+ self.rule_complemented_choquet(ass)
3356
+ elif c_type == ConceptType.SUGENO_INTEGRAL:
3357
+ self.rule_sugeno(ass)
3358
+ elif OperatorConcept.is_not_sugeno(ci):
3359
+ self.rule_complemented_sugeno(ass)
3360
+ elif c_type == ConceptType.QUASI_SUGENO_INTEGRAL:
3361
+ self.rule_quasi_sugeno(ass)
3362
+ elif OperatorConcept.is_not_quasi_sugeno(ci):
3363
+ self.rule_complemented_quasi_sugeno(ass)
3364
+ elif c_type == ConceptType.W_MIN:
3365
+ self.rule_weighted_min(ass)
3366
+ elif OperatorConcept.is_not_weighted_min(ci):
3367
+ self.rule_complemented_weighted_min(ass)
3368
+ elif c_type == ConceptType.W_MAX:
3369
+ self.rule_weighted_max(ass)
3370
+ elif OperatorConcept.is_not_weighted_max(ci):
3371
+ self.rule_complemented_weighted_max(ass)
3139
3372
  else:
3140
- self.get_correct_version_of_individual(ass)
3141
- if ass.get_individual().is_blockable():
3142
- Util.debug(
3143
- f"Direct Blocking status {typing.cast(CreatedIndividual, ass.get_individual()).directly_blocked}"
3144
- )
3145
- Util.debug(
3146
- f"Indirect Blocking status {typing.cast(CreatedIndividual, ass.get_individual()).indirectly_blocked}"
3147
- )
3148
- if (
3149
- ass.get_individual().is_blockable()
3150
- and CreatedIndividualHandler.is_indirectly_blocked(
3151
- typing.cast(CreatedIndividual, ass.get_individual()), self
3152
- )
3153
- ):
3154
- name: str = str(ass.get_individual())
3155
- Util.debug(
3156
- "Skipping assertion (it has an indirectly blocked individual)"
3157
- )
3158
- self.blocked_assertions[name] = self.blocked_assertions.get(
3159
- name, []
3160
- ) + [ass]
3161
- else:
3162
- self.milp.add_new_constraint(ass)
3163
- if self.is_assertion_processed(ass):
3164
- Util.debug(
3165
- f"Assertion (without the degree): {ass} already processed."
3166
- )
3167
- else:
3168
- ind: Individual = ass.get_individual()
3169
- ci: Concept = ass.get_concept()
3170
- self.add_negated_equations(ind, ci)
3171
- c_type: ConceptType = ass.get_type()
3172
- if c_type == ConceptType.ATOMIC:
3173
- self.rule_atomic(ass)
3174
- elif ci.is_complemented_atomic():
3175
- self.rule_complemented_atomic(ass)
3176
- elif c_type == ConceptType.AND:
3177
- self.rule_and(ass)
3178
- elif c_type == ConceptType.OR:
3179
- self.rule_or(ass)
3180
- elif c_type in (ConceptType.SOME, ConceptType.HAS_VALUE):
3181
- self.exist_assertions.append(ass)
3182
- continue
3183
- elif c_type == ConceptType.ALL:
3184
- self.rule_all(ass)
3185
- elif OperatorConcept.is_not_has_value(ci):
3186
- self.rule_complemented_has_value(ass)
3187
- elif c_type == ConceptType.CONCRETE:
3188
- self.rule_concrete(ass)
3189
- elif OperatorConcept.is_not_concrete(ci):
3190
- self.rule_complemented_concrete(ass)
3191
- elif c_type == ConceptType.FUZZY_NUMBER:
3192
- self.rule_fuzzy_number(ass)
3193
- elif OperatorConcept.is_not_fuzzy_number(ci):
3194
- self.rule_complemented_fuzzy_number(ass)
3195
- elif c_type == ConceptType.MODIFIED:
3196
- self.rule_modified(ass)
3197
- elif OperatorConcept.is_not_modified(ci):
3198
- self.rule_complemented_modified(ass)
3199
- elif c_type == ConceptType.TOP:
3200
- self.rule_top(ass)
3201
- elif c_type == ConceptType.BOTTOM:
3202
- self.rule_bottom(ass)
3203
- elif c_type in (
3204
- ConceptType.AT_MOST_VALUE,
3205
- ConceptType.AT_LEAST_VALUE,
3206
- ConceptType.EXACT_VALUE,
3207
- ):
3208
- self.positive_concrete_value_assertions.append(ass)
3209
- elif (
3210
- OperatorConcept.is_not_at_least_value(ci)
3211
- or OperatorConcept.is_not_at_most_value(ci)
3212
- or OperatorConcept.is_not_exact_value(ci)
3213
- ):
3214
- self.add_negated_datatype_restriction(ass)
3215
- elif c_type == ConceptType.SELF:
3216
- self.rule_self(ass)
3217
- elif OperatorConcept.is_not_self(ci):
3218
- self.rule_complemented_self(ass)
3219
- elif c_type == ConceptType.UPPER_APPROX:
3220
- self.rule_upper_approximation(ass)
3221
- elif c_type == ConceptType.TIGHT_UPPER_APPROX:
3222
- self.rule_tight_upper_approximation(ass)
3223
- elif c_type == ConceptType.LOOSE_UPPER_APPROX:
3224
- self.rule_loose_upper_approximation(ass)
3225
- elif c_type == ConceptType.LOWER_APPROX:
3226
- self.rule_lower_approximation(ass)
3227
- elif c_type == ConceptType.TIGHT_LOWER_APPROX:
3228
- self.rule_tight_lower_approximation(ass)
3229
- elif c_type == ConceptType.LOOSE_LOWER_APPROX:
3230
- self.rule_loose_lower_approximation(ass)
3231
- elif c_type == ConceptType.GOEDEL_AND:
3232
- self.rule_goedel_and(ass)
3233
- elif c_type == ConceptType.LUKASIEWICZ_AND:
3234
- self.rule_lukasiewicz_and(ass)
3235
- elif c_type == ConceptType.GOEDEL_OR:
3236
- self.rule_goedel_or(ass)
3237
- elif c_type == ConceptType.LUKASIEWICZ_OR:
3238
- self.rule_lukasiewicz_or(ass)
3239
- elif c_type == ConceptType.GOEDEL_IMPLIES:
3240
- self.rule_goedel_implication(ass)
3241
- elif OperatorConcept.is_not_goedel_implies(ci):
3242
- self.rule_complemented_goedel_implication(ass)
3243
- elif c_type == ConceptType.ZADEH_IMPLIES:
3244
- self.rule_zadeh_implication(ass)
3245
- elif OperatorConcept.is_not_zadeh_implies(ci):
3246
- self.rule_complemented_zadeh_implication(ass)
3247
- elif c_type == ConceptType.W_SUM:
3248
- self.rule_weighted_sum(ass)
3249
- elif OperatorConcept.is_not_weighted_sum(ci):
3250
- self.rule_complemented_weighted_sum(ass)
3251
- elif c_type == ConceptType.W_SUM_ZERO:
3252
- self.rule_weighted_sum_zero(ass)
3253
- elif OperatorConcept.is_not_weighted_sum_zero(ci):
3254
- self.rule_complemented_weighted_sum_zero(ass)
3255
- elif c_type == ConceptType.WEIGHTED:
3256
- self.rule_weighted_concept(ass)
3257
- elif OperatorConcept.is_not_weighted(ci):
3258
- self.rule_complemented_weighted_concept(ass)
3259
- elif c_type == ConceptType.POS_THRESHOLD:
3260
- self.rule_positive_threshold(ass)
3261
- elif OperatorConcept.is_not_pos_threshold(ci):
3262
- self.rule_complemented_positive_threshold(ass)
3263
- elif c_type == ConceptType.NEG_THRESHOLD:
3264
- self.rule_negative_threshold(ass)
3265
- elif OperatorConcept.is_not_neg_threshold(ci):
3266
- self.rule_complemented_negative_threshold(ass)
3267
- elif c_type == ConceptType.EXT_POS_THRESHOLD:
3268
- self.rule_extended_positive_threshold(ass)
3269
- elif OperatorConcept.is_not_ext_pos_threshold(ci):
3270
- self.rule_complemented_extended_positive_threshold(ass)
3271
- elif c_type == ConceptType.EXT_NEG_THRESHOLD:
3272
- self.rule_extended_negative_threshold(ass)
3273
- elif OperatorConcept.is_not_ext_neg_threshold(ci):
3274
- self.rule_complemented_extended_negative_threshold(ass)
3275
- elif c_type == ConceptType.OWA:
3276
- self.rule_owa(ass)
3277
- elif OperatorConcept.is_not_owa(ci):
3278
- self.rule_complemented_owa(ass)
3279
- elif c_type == ConceptType.QUANTIFIED_OWA:
3280
- self.rule_quantified_owa(ass)
3281
- elif OperatorConcept.is_not_qowa(ci):
3282
- self.rule_complemented_quantified_owa(ass)
3283
- elif c_type == ConceptType.CHOQUET_INTEGRAL:
3284
- self.rule_choquet(ass)
3285
- elif OperatorConcept.is_not_choquet(ci):
3286
- self.rule_complemented_choquet(ass)
3287
- elif c_type == ConceptType.SUGENO_INTEGRAL:
3288
- self.rule_sugeno(ass)
3289
- elif OperatorConcept.is_not_sugeno(ci):
3290
- self.rule_complemented_sugeno(ass)
3291
- elif c_type == ConceptType.QUASI_SUGENO_INTEGRAL:
3292
- self.rule_quasi_sugeno(ass)
3293
- elif OperatorConcept.is_not_quasi_sugeno(ci):
3294
- self.rule_complemented_quasi_sugeno(ass)
3295
- elif c_type == ConceptType.W_MIN:
3296
- self.rule_weighted_min(ass)
3297
- elif OperatorConcept.is_not_weighted_min(ci):
3298
- self.rule_complemented_weighted_min(ass)
3299
- elif c_type == ConceptType.W_MAX:
3300
- self.rule_weighted_max(ass)
3301
- elif OperatorConcept.is_not_weighted_max(ci):
3302
- self.rule_complemented_weighted_max(ass)
3303
- else:
3304
- Util.warning(f"Warning: Assertion with type {c_type}")
3305
-
3306
- nodes: set[str] = self.labels_with_nodes.get(str(ind))
3307
- if nodes is not None:
3308
- for node in nodes:
3309
- self.rule_ass_nom(ind, ci, node)
3310
-
3311
- self.mark_process_assertion(ass)
3312
- ind.add_concept(ci)
3313
- Util.debug(
3314
- f"{constants.SEPARATOR}Assertion completed{constants.SEPARATOR}"
3315
- )
3373
+ Util.warning(f"Warning: Assertion with type {c_type}")
3374
+
3375
+ # For each node in labelsWithNodes, apply AssNom rule
3376
+ nodes: set[str] = self.labels_with_nodes.get(str(ind))
3377
+ if nodes is not None:
3378
+ for node in nodes:
3379
+ self.rule_ass_nom(ind, ci, node)
3380
+
3381
+ self.mark_process_assertion(ass)
3382
+ ind.add_concept(ci)
3383
+ Util.debug(
3384
+ f"{constants.SEPARATOR}Assertion completed{constants.SEPARATOR}"
3385
+ )
3316
3386
  self.assertions.clear()
3387
+
3388
+ # Solve one some rule
3317
3389
  if len(self.assertions) == 0:
3318
3390
  self.solve_one_exist_assertion()
3391
+
3392
+ # Check if there are more assertions
3319
3393
  if len(self.assertions) == 0 and len(self.exist_assertions) == 0:
3320
3394
  break
3395
+ # Concrete assertions
3321
3396
  self.solve_concrete_value_assertions()
3322
3397
 
3323
3398
  def solve_concept_assertion(self, ind: Individual, concept: Concept) -> None:
@@ -3355,25 +3430,31 @@ class KnowledgeBase:
3355
3430
  def solve_concept_complemented_assertion(
3356
3431
  self, ind: Individual, lower_limit: Degree, concept: Concept
3357
3432
  ) -> None:
3358
- if isinstance(concept, ChoquetIntegral):
3433
+ if OperatorConcept.is_not_choquet(concept):
3359
3434
  self.solve_choquet_integral_complemented_assertion(ind, concept)
3360
- elif isinstance(concept, (SugenoIntegral, QsugenoIntegral)):
3435
+ elif OperatorConcept.is_not_sugeno(
3436
+ concept
3437
+ ) or OperatorConcept.is_not_quasi_sugeno(concept):
3361
3438
  self.solve_sugeno_integral_complemented_assertion(ind, concept)
3362
- elif isinstance(concept, (OwaConcept, QowaConcept)):
3439
+ elif OperatorConcept.is_not_owa(concept) or OperatorConcept.is_not_qowa(
3440
+ concept
3441
+ ):
3363
3442
  self.solve_owa_complemented_assertion(ind, concept)
3364
- elif isinstance(concept, WeightedMaxConcept):
3443
+ elif OperatorConcept.is_not_weighted_max(concept):
3365
3444
  self.solve_w_max_complemented_assertion(ind, concept)
3366
- elif isinstance(concept, WeightedMinConcept):
3445
+ elif OperatorConcept.is_not_weighted_min(concept):
3367
3446
  self.solve_w_min_complemented_assertion(ind, concept)
3368
- elif isinstance(concept, WeightedSumConcept):
3447
+ elif OperatorConcept.is_not_weighted_sum(concept):
3369
3448
  self.solve_w_sum_complemented_assertion(ind, concept)
3370
- elif isinstance(concept, WeightedSumZeroConcept):
3449
+ elif OperatorConcept.is_not_weighted_sum_zero(concept):
3371
3450
  self.solve_w_sum_zero_complemented_assertion(ind, concept)
3372
- elif isinstance(concept, (ModifiedConcept, ModifiedConcreteConcept)):
3373
- self.solve_modifier_complemented_assertion(
3374
- ind, concept.curr_concept, lower_limit
3375
- )
3376
- elif isinstance(concept, FuzzyConcreteConcept):
3451
+ elif isinstance(concept, OperatorConcept) and isinstance(
3452
+ concept.get_atom(), (ModifiedConcept, ModifiedConcreteConcept)
3453
+ ):
3454
+ self.solve_modifier_complemented_assertion(ind, concept, lower_limit)
3455
+ elif isinstance(concept, OperatorConcept) and isinstance(
3456
+ concept.get_atom(), FuzzyConcreteConcept
3457
+ ):
3377
3458
  self.solve_fuzzy_concrete_concept_complement_assertion(
3378
3459
  ind, lower_limit, concept
3379
3460
  )
@@ -3417,12 +3498,15 @@ class KnowledgeBase:
3417
3498
  self.milp.add_new_constraint(exp, InequalityType.EQUAL, degree)
3418
3499
 
3419
3500
  def solve_choquet_integral_complemented_assertion(
3420
- self, ind: Individual, c: ChoquetIntegral
3501
+ self, ind: Individual, c: OperatorConcept
3421
3502
  ) -> None:
3422
- n: int = len(c.concepts)
3503
+ assert isinstance(c.get_atom(), ChoquetIntegral)
3504
+ ci: ChoquetIntegral = c.get_atom()
3505
+
3506
+ n: int = len(ci.concepts)
3423
3507
  x: list[Variable] = [None] * n
3424
3508
  for i in range(n):
3425
- not_ci = -c.concepts[i]
3509
+ not_ci = -ci.concepts[i]
3426
3510
  x[i] = self.milp.get_variable(ind, not_ci)
3427
3511
  self.add_assertion(ind, not_ci, DegreeVariable.get_degree(x[i]))
3428
3512
 
@@ -3433,9 +3517,9 @@ class KnowledgeBase:
3433
3517
  y: list[Variable] = self.milp.get_ordered_permutation(x, z)
3434
3518
 
3435
3519
  exp = Expression(1.0)
3436
- exp.add_term(Term(-c.weights[0], y[0]))
3520
+ exp.add_term(Term(-ci.weights[0], y[0]))
3437
3521
  for k in range(1, n):
3438
- exp.add_term(Term(c.weights[k - 1] - c.weights[k], y[k]))
3522
+ exp.add_term(Term(ci.weights[k - 1] - ci.weights[k], y[k]))
3439
3523
 
3440
3524
  degree: DegreeVariable = DegreeVariable.get_degree(
3441
3525
  self.milp.get_variable(ind, c)
@@ -3489,8 +3573,11 @@ class KnowledgeBase:
3489
3573
  self.milp.add_new_constraint(exp, InequalityType.EQUAL, degree)
3490
3574
 
3491
3575
  def solve_owa_complemented_assertion(
3492
- self, ind: Individual, c: typing.Union[OwaConcept, QowaConcept]
3576
+ self, ind: Individual, curr_concept: OperatorConcept
3493
3577
  ) -> None:
3578
+ assert isinstance(curr_concept.get_atom(), (OwaConcept, QowaConcept))
3579
+
3580
+ c: typing.Union[OwaConcept, QowaConcept] = curr_concept.get_atom()
3494
3581
  x_A_in_not_WS: Variable = self.milp.get_variable(ind, c)
3495
3582
  n: int = len(c.concepts)
3496
3583
  x: list[Variable] = []
@@ -3509,7 +3596,7 @@ class KnowledgeBase:
3509
3596
  for j in range(n):
3510
3597
  exp.add_term(Term(-c.weights[j], y[j]))
3511
3598
  self.milp.add_new_constraint(exp, InequalityType.EQUAL)
3512
- self.rule_complemented(ind, c)
3599
+ self.rule_complemented(ind, curr_concept)
3513
3600
 
3514
3601
  def solve_sugeno_integral_assertion(
3515
3602
  self, ind: Individual, concept: typing.Union[SugenoIntegral, QsugenoIntegral]
@@ -3561,12 +3648,12 @@ class KnowledgeBase:
3561
3648
  self.milp.get_new_variable(VariableType.SEMI_CONTINUOUS) for _ in range(n)
3562
3649
  ]
3563
3650
 
3564
- if isinstance(concept, SugenoIntegral):
3565
- for i in range(n):
3566
- ZadehSolver.and_equation(c[i], y[i], a[i], self.milp)
3567
- elif isinstance(concept, QsugenoIntegral):
3651
+ if isinstance(concept, QsugenoIntegral):
3568
3652
  for i in range(n):
3569
3653
  LukasiewiczSolver.and_equation(c[i], y[i], a[i], self.milp)
3654
+ elif isinstance(concept, SugenoIntegral):
3655
+ for i in range(n):
3656
+ ZadehSolver.and_equation(c[i], y[i], a[i], self.milp)
3570
3657
 
3571
3658
  degree: DegreeVariable = DegreeVariable.get_degree(
3572
3659
  self.milp.get_variable(ind, concept)
@@ -3587,8 +3674,11 @@ class KnowledgeBase:
3587
3674
  self.milp.add_new_constraint(exp, InequalityType.EQUAL, n - 1)
3588
3675
 
3589
3676
  def solve_sugeno_integral_complemented_assertion(
3590
- self, ind: Individual, concept: typing.Union[SugenoIntegral, QsugenoIntegral]
3677
+ self, ind: Individual, curr_concept: OperatorConcept
3591
3678
  ) -> None:
3679
+ assert isinstance(curr_concept.get_atom(), (SugenoIntegral, QsugenoIntegral))
3680
+ concept: typing.Union[SugenoIntegral, QsugenoIntegral] = curr_concept.get_atom()
3681
+
3592
3682
  n: int = len(concept.concepts)
3593
3683
  x: list[Variable] = []
3594
3684
  for i in range(n):
@@ -3638,15 +3728,15 @@ class KnowledgeBase:
3638
3728
  self.milp.get_new_variable(VariableType.SEMI_CONTINUOUS) for _ in range(n)
3639
3729
  ]
3640
3730
 
3641
- if isinstance(concept, SugenoIntegral):
3642
- for i in range(n):
3643
- ZadehSolver.and_equation(c[i], y[i], a[i], self.milp)
3644
- elif isinstance(concept, QsugenoIntegral):
3731
+ if isinstance(concept, QsugenoIntegral):
3645
3732
  for i in range(n):
3646
3733
  LukasiewiczSolver.and_equation(c[i], y[i], a[i], self.milp)
3734
+ elif isinstance(concept, SugenoIntegral):
3735
+ for i in range(n):
3736
+ ZadehSolver.and_equation(c[i], y[i], a[i], self.milp)
3647
3737
 
3648
3738
  degree: DegreeVariable = DegreeVariable.get_degree(
3649
- self.milp.get_variable(ind, concept)
3739
+ self.milp.get_variable(ind, curr_concept)
3650
3740
  )
3651
3741
  b: list[Variable] = [
3652
3742
  self.milp.get_new_variable(VariableType.BINARY) for _ in range(n)
@@ -3662,7 +3752,7 @@ class KnowledgeBase:
3662
3752
  for i in range(n):
3663
3753
  exp.add_term(Term(1.0, b[i]))
3664
3754
  self.milp.add_new_constraint(exp, InequalityType.EQUAL, n - 1)
3665
- self.rule_complemented(ind, concept)
3755
+ self.rule_complemented(ind, curr_concept)
3666
3756
 
3667
3757
  def solve_w_max_assertion(
3668
3758
  self, ind: Individual, concept: WeightedMaxConcept
@@ -3678,9 +3768,12 @@ class KnowledgeBase:
3678
3768
  ZadehSolver.or_equation(min_vars, x_A_in_WS, self.milp)
3679
3769
 
3680
3770
  def solve_w_max_complemented_assertion(
3681
- self, ind: Individual, concept: WeightedMaxConcept
3771
+ self, ind: Individual, curr_concept: OperatorConcept
3682
3772
  ) -> None:
3683
- x_A_in_WS: Variable = self.milp.get_variable(ind, concept)
3773
+ assert isinstance(curr_concept.get_atom(), WeightedMaxConcept)
3774
+ concept: WeightedMaxConcept = curr_concept.get_atom()
3775
+
3776
+ x_A_in_WS: Variable = self.milp.get_variable(ind, curr_concept)
3684
3777
  negmin: list[Variable] = []
3685
3778
  for ci, weight in zip(concept.concepts, concept.weights):
3686
3779
  not_ci: Concept = -ci
@@ -3691,7 +3784,7 @@ class KnowledgeBase:
3691
3784
  ZadehSolver.or_negated_equation(max_var, xi, 1.0 - weight, self.milp)
3692
3785
  negmin.append(max_var)
3693
3786
  ZadehSolver.and_equation(negmin, x_A_in_WS, self.milp)
3694
- self.rule_complemented(ind, concept)
3787
+ self.rule_complemented(ind, curr_concept)
3695
3788
 
3696
3789
  def solve_w_min_assertion(
3697
3790
  self, ind: Individual, concept: WeightedMinConcept
@@ -3707,9 +3800,12 @@ class KnowledgeBase:
3707
3800
  ZadehSolver.and_equation(max_vars, x_A_in_WS, self.milp)
3708
3801
 
3709
3802
  def solve_w_min_complemented_assertion(
3710
- self, ind: Individual, concept: WeightedMinConcept
3803
+ self, ind: Individual, curr_concept: OperatorConcept
3711
3804
  ) -> None:
3712
- x_A_in_WS: Variable = self.milp.get_variable(ind, concept)
3805
+ assert isinstance(curr_concept.get_atom(), WeightedMinConcept)
3806
+ concept: WeightedMinConcept = curr_concept.get_atom()
3807
+
3808
+ x_A_in_WS: Variable = self.milp.get_variable(ind, curr_concept)
3713
3809
  negmax: list[Variable] = []
3714
3810
  for ci, weight in zip(concept.concepts, concept.weights):
3715
3811
  not_ci: Concept = -ci
@@ -3720,7 +3816,7 @@ class KnowledgeBase:
3720
3816
  ZadehSolver.and_negated_equation(max_var, xi, weight, self.milp)
3721
3817
  negmax.append(max_var)
3722
3818
  ZadehSolver.or_equation(negmax, x_A_in_WS, self.milp)
3723
- self.rule_complemented(ind, concept)
3819
+ self.rule_complemented(ind, curr_concept)
3724
3820
 
3725
3821
  def solve_w_sum_assertion(
3726
3822
  self, ind: Individual, concept: WeightedSumConcept
@@ -3738,9 +3834,12 @@ class KnowledgeBase:
3738
3834
  )
3739
3835
 
3740
3836
  def solve_w_sum_complemented_assertion(
3741
- self, ind: Individual, concept: WeightedSumConcept
3837
+ self, ind: Individual, curr_concept: OperatorConcept
3742
3838
  ) -> None:
3743
- x_A_in_not_WS: Variable = self.milp.get_variable(ind, concept)
3839
+ assert isinstance(curr_concept.get_atom(), WeightedSumConcept)
3840
+ concept: WeightedSumConcept = curr_concept.get_atom()
3841
+
3842
+ x_A_in_not_WS: Variable = self.milp.get_variable(ind, curr_concept)
3744
3843
  terms: list[Term] = []
3745
3844
  for ci, weight in zip(concept.concepts, concept.weights):
3746
3845
  not_ci: Concept = -ci
@@ -3753,7 +3852,7 @@ class KnowledgeBase:
3753
3852
  InequalityType.EQUAL,
3754
3853
  DegreeVariable.get_degree(x_A_in_not_WS),
3755
3854
  )
3756
- self.rule_complemented(ind, concept)
3855
+ self.rule_complemented(ind, curr_concept)
3757
3856
 
3758
3857
  def solve_w_sum_zero_assertion(
3759
3858
  self, ind: Individual, concept: WeightedSumZeroConcept
@@ -3787,11 +3886,14 @@ class KnowledgeBase:
3787
3886
  self.rule_complemented(ind, concept)
3788
3887
 
3789
3888
  def solve_w_sum_zero_complemented_assertion(
3790
- self, ind: Individual, concept: WeightedSumZeroConcept
3889
+ self, ind: Individual, curr_concept: OperatorConcept
3791
3890
  ) -> None:
3891
+ assert isinstance(curr_concept.get_atom(), WeightedSumZeroConcept)
3892
+ concept: WeightedSumZeroConcept = curr_concept.get_atom()
3893
+
3792
3894
  terms: list[Term] = []
3793
3895
  vx: list[Variable] = []
3794
- x_A_in_not_ws: Variable = self.milp.get_variable(ind, concept)
3896
+ x_A_in_not_ws: Variable = self.milp.get_variable(ind, curr_concept)
3795
3897
  y: Variable = self.milp.get_new_variable(VariableType.BINARY)
3796
3898
  z: Variable = self.milp.get_new_variable(VariableType.SEMI_CONTINUOUS)
3797
3899
  for ci, weight in zip(concept.concepts, concept.weights):
@@ -3814,7 +3916,7 @@ class KnowledgeBase:
3814
3916
  exp2: Expression = Expression(*terms)
3815
3917
  exp2.add_term(Term(1.0, y))
3816
3918
  self.milp.add_new_constraint(exp2, InequalityType.GREATER_THAN)
3817
- self.rule_complemented(ind, concept)
3919
+ self.rule_complemented(ind, curr_concept)
3818
3920
 
3819
3921
  def solve_crisp_concrete_concept_assertion(
3820
3922
  self, ind: Individual, concept: CrispConcreteConcept
@@ -3888,10 +3990,11 @@ class KnowledgeBase:
3888
3990
  self,
3889
3991
  ind: CreatedIndividual,
3890
3992
  lower_limit: Degree,
3891
- concept: FuzzyConcreteConcept,
3993
+ curr_concept: OperatorConcept,
3892
3994
  ) -> None:
3893
3995
  """Solve a complement assertion"""
3894
- assertion: Assertion = Assertion(ind, -concept, lower_limit)
3996
+ assert isinstance(curr_concept.get_atom(), FuzzyConcreteConcept)
3997
+ assertion: Assertion = Assertion(ind, curr_concept, lower_limit)
3895
3998
  self.rule_complemented_complex_assertion(assertion)
3896
3999
 
3897
4000
  def solve_left_concrete_concept_assertion(
@@ -4264,9 +4367,12 @@ class KnowledgeBase:
4264
4367
  )
4265
4368
 
4266
4369
  def solve_modifier_complemented_assertion(
4267
- self, ind: Individual, con: Concept, degree: Degree
4370
+ self, ind: Individual, concept: OperatorConcept, degree: Degree
4268
4371
  ) -> None:
4269
- ass: Assertion = Assertion(ind, -con, degree)
4372
+ assert isinstance(concept, OperatorConcept) and isinstance(
4373
+ concept.get_atom(), (ModifiedConcept, ModifiedConcreteConcept)
4374
+ )
4375
+ ass: Assertion = Assertion(ind, concept, degree)
4270
4376
  self.rule_complemented_complex_assertion(ass)
4271
4377
 
4272
4378
  def solve_linear_modifier_assertion(
@@ -4434,7 +4540,9 @@ class KnowledgeBase:
4434
4540
 
4435
4541
  def add_negated_datatype_restriction(self, ass: Assertion) -> None:
4436
4542
  a: Individual = ass.get_individual()
4437
- c: Concept = ass.get_concept()
4543
+ op: Concept = ass.get_concept()
4544
+ assert isinstance(op, OperatorConcept) and op.type == ConceptType.COMPLEMENT
4545
+ c: Concept = op.get_atom()
4438
4546
  assert isinstance(c, HasRoleInterface)
4439
4547
  f_name: str = c.role
4440
4548
  a.add_concrete_restriction(f_name, ass)
@@ -5372,7 +5480,7 @@ class KnowledgeBase:
5372
5480
  self.compute_variables_old_calculus(fcc)
5373
5481
  ind: CreatedIndividual = typing.cast(CreatedIndividual, ass.get_individual())
5374
5482
 
5375
- self.solve_concept_complemented_assertion(ind, ass.get_lower_limit(), fcc)
5483
+ self.solve_concept_complemented_assertion(ind, ass.get_lower_limit(), c)
5376
5484
  # fcc.solve_complement_assertion(ind, ass.get_lower_limit(), self)
5377
5485
 
5378
5486
  def rule_fuzzy_number(self, ass: Assertion) -> None:
@@ -5413,7 +5521,7 @@ class KnowledgeBase:
5413
5521
  # ass.get_individual(), ass.get_lower_limit(), self
5414
5522
  # )
5415
5523
  self.solve_modifier_complemented_assertion(
5416
- ass.get_individual(), mod, ass.get_lower_limit()
5524
+ ass.get_individual(), c, ass.get_lower_limit()
5417
5525
  )
5418
5526
 
5419
5527
  def rule_bottom(self, ass: Assertion) -> None:
@@ -5786,7 +5894,7 @@ class KnowledgeBase:
5786
5894
  self.milp.add_new_constraint(
5787
5895
  Expression(Term(1.0, x_a_in_wc), Term(-w, x_a_in_c)), InequalityType.EQUAL
5788
5896
  )
5789
- self.rule_complemented(i, wc)
5897
+ self.rule_complemented(i, c)
5790
5898
 
5791
5899
  def rule_complemented_complex_assertion(self, ass: Assertion) -> None:
5792
5900
  i: Individual = ass.get_individual()
@@ -5818,12 +5926,13 @@ class KnowledgeBase:
5818
5926
  c: Concept = ass.get_concept()
5819
5927
  assert isinstance(c, OperatorConcept)
5820
5928
  ws: WeightedSumConcept = typing.cast(WeightedSumConcept, c.get_atom())
5929
+
5821
5930
  n: int = len(ws.concepts)
5822
5931
  self.old_01_variables += n
5823
5932
  self.rules_applied[KnowledgeBaseRules.RULE_NOT_W_SUM] += 1
5824
5933
 
5825
5934
  # ws.solve_complemented_assertion(ass.get_individual(), self)
5826
- self.solve_concept_complemented_assertion(ass.get_individual(), None, ws)
5935
+ self.solve_concept_complemented_assertion(ass.get_individual(), None, c)
5827
5936
 
5828
5937
  def rule_weighted_sum_zero(self, ass: Assertion) -> None:
5829
5938
  n: int = len(typing.cast(WeightedSumZeroConcept, ass.get_concept()).concepts)
@@ -5839,11 +5948,11 @@ class KnowledgeBase:
5839
5948
  def rule_complemented_weighted_sum_zero(self, ass: Assertion) -> None:
5840
5949
  c: Concept = ass.get_concept()
5841
5950
  assert isinstance(c, OperatorConcept)
5842
- wsz: WeightedSumZeroConcept = typing.cast(WeightedSumZeroConcept, c.get_atom())
5951
+ assert isinstance(c.get_atom(), WeightedSumZeroConcept)
5843
5952
 
5844
5953
  self.rules_applied[KnowledgeBaseRules.RULE_NOT_W_SUM_ZERO] += 1
5845
5954
  # wsz.solve_complemented_assertion(ass.get_individual(), self)
5846
- self.solve_concept_complemented_assertion(ass.get_individual(), None, wsz)
5955
+ self.solve_concept_complemented_assertion(ass.get_individual(), None, c)
5847
5956
 
5848
5957
  def rule_weighted_min(self, ass: Assertion) -> None:
5849
5958
  self.rules_applied[KnowledgeBaseRules.RULE_W_MIN] += 1
@@ -5857,11 +5966,11 @@ class KnowledgeBase:
5857
5966
  def rule_complemented_weighted_min(self, ass: Assertion) -> None:
5858
5967
  c: Concept = ass.get_concept()
5859
5968
  assert isinstance(c, OperatorConcept)
5860
- wm: WeightedMinConcept = typing.cast(WeightedMinConcept, c.get_atom())
5969
+ assert isinstance(c.get_atom(), WeightedMinConcept)
5861
5970
 
5862
5971
  self.rules_applied[KnowledgeBaseRules.RULE_NOT_W_MIN] += 1
5863
5972
  # wm.solve_complemented_assertion(ass.get_individual(), self)
5864
- self.solve_concept_complemented_assertion(ass.get_individual(), None, wm)
5973
+ self.solve_concept_complemented_assertion(ass.get_individual(), None, c)
5865
5974
 
5866
5975
  def rule_weighted_max(self, ass: Assertion) -> None:
5867
5976
  self.rules_applied[KnowledgeBaseRules.RULE_W_MAX] += 1
@@ -5875,11 +5984,11 @@ class KnowledgeBase:
5875
5984
  def rule_complemented_weighted_max(self, ass: Assertion) -> None:
5876
5985
  c: Concept = ass.get_concept()
5877
5986
  assert isinstance(c, OperatorConcept)
5878
- wm: WeightedMaxConcept = typing.cast(WeightedMaxConcept, c.get_atom())
5987
+ assert isinstance(c.get_atom(), WeightedMaxConcept)
5879
5988
 
5880
5989
  self.rules_applied[KnowledgeBaseRules.RULE_NOT_W_MAX] += 1
5881
5990
  # wm.solve_complemented_assertion(ass.get_individual(), self)
5882
- self.solve_concept_complemented_assertion(ass.get_individual(), None, wm)
5991
+ self.solve_concept_complemented_assertion(ass.get_individual(), None, c)
5883
5992
 
5884
5993
  def rule_owa(self, ass: Assertion) -> None:
5885
5994
  n: int = len(typing.cast(OwaConcept, ass.get_concept()).concepts)
@@ -5904,7 +6013,7 @@ class KnowledgeBase:
5904
6013
  self.rules_applied[KnowledgeBaseRules.RULE_NOT_OWA] += 1
5905
6014
  # oc.solve_complemented_assertion(ass.get_individual(), self)
5906
6015
 
5907
- self.solve_concept_complemented_assertion(ass.get_individual(), None, oc)
6016
+ self.solve_concept_complemented_assertion(ass.get_individual(), None, c)
5908
6017
 
5909
6018
  def rule_quantified_owa(self, ass: Assertion) -> None:
5910
6019
  self.rules_applied[KnowledgeBaseRules.RULE_OWA] += 1
@@ -5932,12 +6041,12 @@ class KnowledgeBase:
5932
6041
  def rule_complemented_choquet(self, ass: Assertion) -> None:
5933
6042
  c: Concept = ass.get_concept()
5934
6043
  assert isinstance(c, OperatorConcept)
5935
- ci: ChoquetIntegral = typing.cast(ChoquetIntegral, c.get_atom())
6044
+ assert isinstance(c.get_atom(), ChoquetIntegral)
5936
6045
 
5937
6046
  self.rules_applied[KnowledgeBaseRules.RULE_NOT_CHOQUET_INTEGRAL] += 1
5938
6047
  # ci.solve_complemented_assertion(ass.get_individual(), self)
5939
6048
 
5940
- self.solve_concept_complemented_assertion(ass.get_individual(), None, ci)
6049
+ self.solve_concept_complemented_assertion(ass.get_individual(), None, c)
5941
6050
 
5942
6051
  def rule_sugeno(self, ass: Assertion) -> None:
5943
6052
  self.rules_applied[KnowledgeBaseRules.RULE_SUGENO_INTEGRAL] += 1
@@ -5951,11 +6060,11 @@ class KnowledgeBase:
5951
6060
  def rule_complemented_sugeno(self, ass: Assertion) -> None:
5952
6061
  c: Concept = ass.get_concept()
5953
6062
  assert isinstance(c, OperatorConcept)
5954
- si: SugenoIntegral = typing.cast(SugenoIntegral, c.get_atom())
6063
+ assert isinstance(c.get_atom(), SugenoIntegral)
5955
6064
 
5956
6065
  self.rules_applied[KnowledgeBaseRules.RULE_NOT_SUGENO_INTEGRAL] += 1
5957
6066
  # si.solve_complemented_assertion(ass.get_individual(), self)
5958
- self.solve_concept_complemented_assertion(ass.get_individual(), None, si)
6067
+ self.solve_concept_complemented_assertion(ass.get_individual(), None, c)
5959
6068
 
5960
6069
  def rule_quasi_sugeno(self, ass: Assertion) -> None:
5961
6070
  self.rules_applied[KnowledgeBaseRules.RULE_QUASI_SUGENO_INTEGRAL] += 1
@@ -5969,11 +6078,11 @@ class KnowledgeBase:
5969
6078
  def rule_complemented_quasi_sugeno(self, ass: Assertion) -> None:
5970
6079
  c: Concept = ass.get_concept()
5971
6080
  assert isinstance(c, OperatorConcept)
5972
- qsi: QsugenoIntegral = typing.cast(QsugenoIntegral, c.get_atom())
6081
+ assert isinstance(c.get_atom(), QsugenoIntegral)
5973
6082
 
5974
6083
  self.rules_applied[KnowledgeBaseRules.RULE_NOT_QUASI_SUGENO_INTEGRAL] += 1
5975
6084
  # qsi.solve_complemented_assertion(ass.get_individual(), self)
5976
- self.solve_concept_complemented_assertion(ass.get_individual(), None, qsi)
6085
+ self.solve_concept_complemented_assertion(ass.get_individual(), None, c)
5977
6086
 
5978
6087
  def rule_complemented_at_most_datatype_restriction(
5979
6088
  self, b: CreatedIndividual, ass: Assertion
@@ -6002,9 +6111,6 @@ class KnowledgeBase:
6002
6111
  def set_dynamic_blocking(self) -> None:
6003
6112
  self.blocking_dynamic = True
6004
6113
 
6005
- def get_version(self) -> float:
6006
- return KnowledgeBase.VERSION
6007
-
6008
6114
  def is_crisp_role(self, role_name: str) -> bool:
6009
6115
  return (
6010
6116
  constants.KNOWLEDGE_BASE_SEMANTICS == FuzzyLogic.CLASSICAL
@@ -6216,6 +6322,7 @@ class KnowledgeBase:
6216
6322
  Util.debug(f"{constants.STAR_SEPARATOR * 2}")
6217
6323
 
6218
6324
 
6325
+ @class_debugging()
6219
6326
  class DatatypeReasoner:
6220
6327
 
6221
6328
  @staticmethod
@@ -6256,7 +6363,7 @@ class DatatypeReasoner:
6256
6363
  ind, role, b, DegreeVariable.get_degree(x_f), kb
6257
6364
  )
6258
6365
  x_b: Variable = DatatypeReasoner.get_xb(b, t, kb)
6259
- if new_variable is not None and k is not None:
6366
+ if new_variable and k is not None:
6260
6367
  kb.restrict_range(x_b, x_f, k[0], k[1])
6261
6368
  return [b, x_b, x_f]
6262
6369
 
@@ -6495,7 +6602,7 @@ class DatatypeReasoner:
6495
6602
  n, kb, x_b, x_is_c, x_f, k, InequalityType.LESS_THAN
6496
6603
  )
6497
6604
  elif type == InequalityType.GREATER_THAN:
6498
- if isinstance(n, float):
6605
+ if isinstance(n, constants.NUMBER):
6499
6606
  kb.milp.add_new_constraint(
6500
6607
  Expression(
6501
6608
  constants.MAXVAL,
@@ -6524,7 +6631,7 @@ class DatatypeReasoner:
6524
6631
  InequalityType.GREATER_THAN,
6525
6632
  )
6526
6633
  elif type == InequalityType.LESS_THAN:
6527
- if isinstance(n, float):
6634
+ if isinstance(n, constants.NUMBER):
6528
6635
  kb.milp.add_new_constraint(
6529
6636
  Expression(
6530
6637
  -constants.MAXVAL,
@@ -6564,7 +6671,7 @@ class DatatypeReasoner:
6564
6671
  t: ConcreteFeature = DatatypeReasoner.get_feature(f_name, kb)
6565
6672
  k: typing.Optional[list[float]] = DatatypeReasoner.get_bounds(t)
6566
6673
  return_value: list[typing.Any] = (
6567
- DatatypeReasoner.get_created_individual_and_variables(a, c.role, t, k, kb)
6674
+ DatatypeReasoner.get_created_individual_and_variables(a, f_name, t, k, kb)
6568
6675
  )
6569
6676
  b: CreatedIndividual = typing.cast(CreatedIndividual, return_value[0])
6570
6677
  x_b: Variable = typing.cast(Variable, return_value[1])
@@ -6647,7 +6754,8 @@ class DatatypeReasoner:
6647
6754
  type: InequalityType,
6648
6755
  ) -> None:
6649
6756
  if type == InequalityType.GREATER_THAN:
6650
- if isinstance(n, float):
6757
+ if isinstance(n, constants.NUMBER):
6758
+ # x_b <= (n - \epsilon) + (2 k_\infty + \epsilon) (1 - x_f) + (2k_\infty + \epsilon) x_is_c
6651
6759
  kb.milp.add_new_constraint(
6652
6760
  Expression(
6653
6761
  constants.MAXVAL2 + n,
@@ -6658,6 +6766,7 @@ class DatatypeReasoner:
6658
6766
  InequalityType.GREATER_THAN,
6659
6767
  )
6660
6768
  elif isinstance(n, Variable):
6769
+ # x_b <= x - \epsilon x_f + 2k_\infty (1 - x_f) + (2k_\infty + \epsilon) x_is_c
6661
6770
  kb.milp.add_new_constraint(
6662
6771
  Expression(
6663
6772
  constants.MAXVAL2,
@@ -6668,16 +6777,19 @@ class DatatypeReasoner:
6668
6777
  ),
6669
6778
  InequalityType.GREATER_THAN,
6670
6779
  )
6780
+ # x \geq -max_val
6671
6781
  kb.milp.add_new_constraint(
6672
6782
  Expression(constants.MAXVAL, Term(1.0, n)),
6673
6783
  InequalityType.GREATER_THAN,
6674
6784
  )
6785
+ # x \leq max_val
6675
6786
  kb.milp.add_new_constraint(
6676
6787
  Expression(constants.MAXVAL, Term(-1.0, n)),
6677
6788
  InequalityType.GREATER_THAN,
6678
6789
  )
6679
6790
  elif type == InequalityType.LESS_THAN:
6680
- if isinstance(n, float):
6791
+ if isinstance(n, constants.NUMBER):
6792
+ # x_b >= (n + \epsilon) - (2 k_\infty + \epsilon) (1 - x_f) - (2k_\infty + \epsilon) x_is_c
6681
6793
  kb.milp.add_new_constraint(
6682
6794
  Expression(
6683
6795
  -constants.MAXVAL2 + n,
@@ -6688,6 +6800,7 @@ class DatatypeReasoner:
6688
6800
  InequalityType.LESS_THAN,
6689
6801
  )
6690
6802
  elif isinstance(n, Variable):
6803
+ # x_b >= x + \epsilon x_f - 2k_\infty (1 - x_f) - (2k_\infty + \epsilon) x_is_c
6691
6804
  kb.milp.add_new_constraint(
6692
6805
  Expression(
6693
6806
  -constants.MAXVAL2,
@@ -6698,16 +6811,19 @@ class DatatypeReasoner:
6698
6811
  ),
6699
6812
  InequalityType.LESS_THAN,
6700
6813
  )
6814
+ # x \geq -max_val
6701
6815
  kb.milp.add_new_constraint(
6702
6816
  Expression(constants.MAXVAL, Term(1.0, n)),
6703
6817
  InequalityType.GREATER_THAN,
6704
6818
  )
6819
+ # x \leq max_val
6705
6820
  kb.milp.add_new_constraint(
6706
6821
  Expression(constants.MAXVAL, Term(-1.0, n)),
6707
6822
  InequalityType.GREATER_THAN,
6708
6823
  )
6709
6824
  elif type == InequalityType.EQUAL:
6710
- if isinstance(n, float):
6825
+ if isinstance(n, constants.NUMBER):
6826
+ # x_b <= (n - \epsilon) y + k_\infty (1 - y) + (2 k_\infty + \epsilon) (1 - x_f) + (2k_\infty + \epsilon) x_is_c
6711
6827
  y: Variable = kb.milp.get_new_variable(VariableType.BINARY)
6712
6828
  kb.milp.add_new_constraint(
6713
6829
  Expression(
@@ -6719,6 +6835,7 @@ class DatatypeReasoner:
6719
6835
  ),
6720
6836
  InequalityType.GREATER_THAN,
6721
6837
  )
6838
+ # x_b >= (n + \epsilon) (1 - y) - k_\infty y - (2 k_\infty + \epsilon) (1 - x_f) - (2k_\infty + \epsilon) x_is_c
6722
6839
  kb.milp.add_new_constraint(
6723
6840
  Expression(
6724
6841
  -constants.MAXVAL2 + n,
@@ -6730,10 +6847,11 @@ class DatatypeReasoner:
6730
6847
  InequalityType.LESS_THAN,
6731
6848
  )
6732
6849
  elif isinstance(n, Variable):
6850
+ # x_b <= x - \epsilon x_f + (2k_\infty + \epsilon) (1 - y) + 2k_\infty (1 - x_f) + (2k_\infty + \epsilon) x_is_c
6733
6851
  y: Variable = kb.milp.get_new_variable(VariableType.BINARY)
6734
6852
  kb.milp.add_new_constraint(
6735
6853
  Expression(
6736
- 2 * constants.MAXVAL2 + ConfigReader.EPSILON,
6854
+ 4 * constants.MAXVAL + ConfigReader.EPSILON,
6737
6855
  Term(constants.MAXVAL + n + ConfigReader.EPSILON, y),
6738
6856
  Term(-1.0, x_b),
6739
6857
  Term(1.0, n),
@@ -6742,6 +6860,7 @@ class DatatypeReasoner:
6742
6860
  ),
6743
6861
  InequalityType.GREATER_THAN,
6744
6862
  )
6863
+ # x_b >= x + \epsilon x_f - (2k_\infty + \epsilon) y - 2k_\infty (1 - x_f) - (2k_\infty + \epsilon) x_is_c
6745
6864
  kb.milp.add_new_constraint(
6746
6865
  Expression(
6747
6866
  -constants.MAXVAL2,
@@ -6753,10 +6872,12 @@ class DatatypeReasoner:
6753
6872
  ),
6754
6873
  InequalityType.LESS_THAN,
6755
6874
  )
6875
+ # x \geq -maxval
6756
6876
  kb.milp.add_new_constraint(
6757
6877
  Expression(constants.MAXVAL, Term(1.0, n)),
6758
6878
  InequalityType.GREATER_THAN,
6759
6879
  )
6880
+ # x \leq maxval
6760
6881
  kb.milp.add_new_constraint(
6761
6882
  Expression(constants.MAXVAL, Term(-1.0, n)),
6762
6883
  InequalityType.GREATER_THAN,
@@ -6920,27 +7041,23 @@ class DatatypeReasoner:
6920
7041
  b: CreatedIndividual, ass: Assertion, kb: KnowledgeBase, type: InequalityType
6921
7042
  ) -> None:
6922
7043
  a: Individual = ass.get_individual()
7044
+ not_c: Concept = ass.get_concept()
7045
+ assert isinstance(not_c, OperatorConcept)
7046
+ c: Concept = not_c.get_atom()
7047
+ assert isinstance(c, HasValueInterface)
6923
7048
 
6924
- c: Concept = ass.get_concept()
6925
- assert isinstance(c, OperatorConcept)
6926
-
6927
- not_c: Concept = c.get_atom()
6928
- assert isinstance(not_c, HasValueInterface)
6929
-
6930
- f_name: str = not_c.role
7049
+ f_name: str = c.role
6931
7050
  t: ConcreteFeature = DatatypeReasoner.get_feature(f_name, kb)
6932
7051
  k: typing.Optional[list[float]] = DatatypeReasoner.get_bounds(t)
6933
7052
  return_value: list[typing.Any] = (
6934
- DatatypeReasoner.get_created_individual_and_variables(
6935
- a, not_c.role, t, k, kb
6936
- )
7053
+ DatatypeReasoner.get_created_individual_and_variables(a, f_name, t, k, kb)
6937
7054
  )
6938
7055
  x_f: Variable = typing.cast(Variable, return_value[2])
6939
7056
  x_is_not_c: Variable = kb.milp.get_variable(ass)
6940
7057
  kb.old_binary_variables += 1
6941
7058
  x_f.set_binary_variable()
6942
7059
  x_b: Variable = DatatypeReasoner.get_xb(b, t, kb)
6943
- c: Concept = -not_c
7060
+ # c: Concept = -not_c
6944
7061
  x_is_c: Variable = kb.milp.get_variable(a, c)
6945
7062
  x_b.set_datatype_filler_variable()
6946
7063
  kb.milp.add_new_constraint(
@@ -6972,6 +7089,7 @@ class DatatypeReasoner:
6972
7089
  x_is_not_c.set_binary_variable()
6973
7090
  x_is_c.set_binary_variable()
6974
7091
  if isinstance(n, FeatureFunction):
7092
+ # If n is a FeatureFunction, check that there exist fillers
6975
7093
  fun: FeatureFunction = typing.cast(FeatureFunction, n)
6976
7094
  array: set[str] = fun.get_features()
6977
7095
  for feature in array:
@@ -7026,6 +7144,7 @@ class DatatypeReasoner:
7026
7144
  DatatypeReasoner.apply_not_rule(b, ass, kb, InequalityType.EQUAL)
7027
7145
 
7028
7146
 
7147
+ @class_debugging()
7029
7148
  class LukasiewiczSolver:
7030
7149
  @staticmethod
7031
7150
  def solve_and(ass: Assertion, kb: KnowledgeBase) -> None:
@@ -7352,6 +7471,7 @@ class LukasiewiczSolver:
7352
7471
  milp.add_new_constraint(exp2, InequalityType.LESS_THAN)
7353
7472
 
7354
7473
 
7474
+ @class_debugging()
7355
7475
  class ZadehSolver:
7356
7476
 
7357
7477
  @staticmethod
@@ -7849,14 +7969,12 @@ class ZadehSolver:
7849
7969
  else:
7850
7970
  assert isinstance(args[0], Variable)
7851
7971
  assert isinstance(args[1], Variable)
7852
- assert isinstance(args[2], Variable)
7972
+ assert isinstance(args[2], constants.NUMBER)
7853
7973
  assert isinstance(args[3], MILPHelper)
7854
7974
  ZadehSolver.__or_equation_1(*args)
7855
7975
 
7856
7976
  @staticmethod
7857
- def __or_equation_1(
7858
- z: Variable, x1: Variable, x2: Variable, milp: MILPHelper
7859
- ) -> None:
7977
+ def __or_equation_1(z: Variable, x1: Variable, x2: float, milp: MILPHelper) -> None:
7860
7978
  y: Variable = milp.get_new_variable(VariableType.BINARY)
7861
7979
  milp.add_new_constraint(
7862
7980
  Expression(Term(1.0, z), Term(-1.0, x1)), InequalityType.GREATER_THAN
@@ -7936,6 +8054,7 @@ class ZadehSolver:
7936
8054
  return n1
7937
8055
 
7938
8056
 
8057
+ @class_debugging()
7939
8058
  class ClassicalSolver:
7940
8059
  @staticmethod
7941
8060
  def solve_and(ass: Assertion, kb: KnowledgeBase) -> None:
@@ -7954,6 +8073,7 @@ class ClassicalSolver:
7954
8073
  ZadehSolver.solve_all(rel, restrict, kb)
7955
8074
 
7956
8075
 
8076
+ @class_debugging()
7957
8077
  class IndividualHandler:
7958
8078
 
7959
8079
  @staticmethod
@@ -8134,6 +8254,7 @@ class IndividualHandler:
8134
8254
  return
8135
8255
 
8136
8256
 
8257
+ @class_debugging()
8137
8258
  class CreatedIndividualHandler:
8138
8259
 
8139
8260
  @staticmethod
@@ -8167,16 +8288,22 @@ class CreatedIndividualHandler:
8167
8288
  def unblock_pairwise(
8168
8289
  current_individual: CreatedIndividual, kb: KnowledgeBase
8169
8290
  ) -> None:
8291
+ """
8292
+ Unblock the individual
8293
+ """
8170
8294
  Util.debug(f"Test of Pair-wise Unblock children of {current_individual.name}")
8295
+ # "current_individual" is a blocking Y node: unblock blocked nodes
8171
8296
  if current_individual.name in kb.directly_blocked_children:
8172
8297
  Util.debug(f"{current_individual.name} is a blocking Y node")
8298
+ # remove Y from the Yprime list
8173
8299
  y_prime: CreatedIndividual = typing.cast(
8174
8300
  CreatedIndividual,
8175
8301
  typing.cast(CreatedIndividual, current_individual).parent,
8176
8302
  )
8177
8303
  y_individuals: list[str] = kb.y_prime_individuals.get(str(y_prime), [])
8178
- del y_individuals[current_individual.name]
8304
+ y_individuals.remove(current_individual.name)
8179
8305
 
8306
+ # update Xprime list
8180
8307
  if len(y_individuals) > 0:
8181
8308
  kb.y_prime_individuals[str(y_prime)] = y_individuals
8182
8309
  else:
@@ -8184,6 +8311,7 @@ class CreatedIndividualHandler:
8184
8311
 
8185
8312
  for x_name in kb.directly_blocked_children.get(current_individual.name):
8186
8313
  Util.debug(f"Processing X node {x_name}")
8314
+ # remove Xname from the Xprime list
8187
8315
  x: CreatedIndividual = typing.cast(
8188
8316
  CreatedIndividual, kb.individuals.get(x_name)
8189
8317
  )
@@ -8191,22 +8319,25 @@ class CreatedIndividualHandler:
8191
8319
  CreatedIndividual, x.get_parent()
8192
8320
  )
8193
8321
  x_individuals: list[str] = kb.x_prime_individuals.get(str(x_prime), [])
8194
- del x_individuals[current_individual.name]
8195
-
8322
+ x_individuals.remove(x_name)
8196
8323
  if len(x_individuals) > 0:
8197
8324
  kb.x_prime_individuals[str(x_prime)] = x_individuals
8198
8325
  else:
8199
8326
  del kb.x_prime_individuals[str(x_prime)]
8327
+ # at last, unblock
8200
8328
  kb.unblock_individual(x_name)
8201
8329
 
8330
+ # now, Y (= current_individual) cannot be a blocking node anymore
8202
8331
  del kb.directly_blocked_children[current_individual.name]
8203
8332
 
8333
+ # if "current_individual" is a Yprime node: unblock blocking Y nodes
8204
8334
  if current_individual.name in kb.y_prime_individuals:
8205
8335
  Util.debug(f"{current_individual.name} is a y_prime node")
8206
8336
  for y_name in kb.y_prime_individuals.get(current_individual.name):
8207
8337
  Util.debug(f"Processing Y node {y_name}")
8208
8338
  for x_name in kb.directly_blocked_children.get(y_name):
8209
8339
  Util.debug(f"Processing X node {x_name}")
8340
+ # remove X from the Xprime list
8210
8341
  x: CreatedIndividual = typing.cast(
8211
8342
  CreatedIndividual, kb.individuals.get(x_name)
8212
8343
  )
@@ -8219,17 +8350,19 @@ class CreatedIndividualHandler:
8219
8350
  x_individuals: list[str] = kb.x_prime_individuals.get(
8220
8351
  str(x_prime), []
8221
8352
  )
8222
- del x_individuals[current_individual.name]
8353
+ x_individuals.remove(x_name)
8223
8354
  if len(x_individuals) > 0:
8224
8355
  kb.x_prime_individuals[str(x_prime)] = x_individuals
8225
8356
  else:
8226
8357
  del kb.x_prime_individuals[str(x_prime)]
8227
-
8358
+ # unblock X
8228
8359
  kb.unblock_individual(x_name)
8229
-
8360
+ # now, Yname cannot be a blocking node anymore
8230
8361
  del kb.directly_blocked_children[y_name]
8362
+ # now, remove Yprime from the Yprime list
8231
8363
  del kb.y_prime_individuals[current_individual.name]
8232
8364
 
8365
+ # if "current_individual" is a Xprime node: unblock blocked X nodes
8233
8366
  if current_individual.name in kb.x_prime_individuals:
8234
8367
  Util.debug(f"{current_individual.name} is a x_prime node")
8235
8368
  x_individuals: list[str] = kb.x_prime_individuals.get(
@@ -8237,6 +8370,7 @@ class CreatedIndividualHandler:
8237
8370
  )
8238
8371
  for x_name in x_individuals:
8239
8372
  Util.debug(f"Processing X node {x_name}")
8373
+ # remove X from the directlyBlockedChildren list
8240
8374
  x: CreatedIndividual = typing.cast(
8241
8375
  CreatedIndividual, kb.individuals.get(x_name)
8242
8376
  )
@@ -8246,26 +8380,28 @@ class CreatedIndividualHandler:
8246
8380
  CreatedIndividual, kb.individuals.get(y_name)
8247
8381
  )
8248
8382
  blocked_by_y: list[str] = kb.directly_blocked_children[y_name]
8249
- del blocked_by_y[x_name]
8383
+ blocked_by_y.remove(x_name)
8250
8384
 
8251
8385
  if len(blocked_by_y) > 0:
8252
8386
  kb.directly_blocked_children[y_name] = blocked_by_y
8253
8387
  else:
8254
8388
  del kb.directly_blocked_children[y_name]
8389
+ # update Yprime list
8255
8390
  y_prime: CreatedIndividual = typing.cast(
8256
8391
  CreatedIndividual, y.get_parent()
8257
8392
  )
8258
8393
  y_individuals: list[str] = kb.x_prime_individuals.get(
8259
8394
  str(y_prime)
8260
8395
  )
8261
- del y_individuals[y_name]
8396
+ y_individuals.remove(y_name)
8262
8397
 
8263
8398
  if len(y_individuals) > 0:
8264
8399
  kb.y_prime_individuals[str(y_prime)] = x_individuals
8265
8400
  else:
8266
8401
  del kb.y_prime_individuals[str(y_prime)]
8267
-
8402
+ # unblock X
8268
8403
  kb.unblock_individual(x_name)
8404
+ # now, remove Xprime from the Xprime list
8269
8405
  del kb.x_prime_individuals[current_individual.name]
8270
8406
 
8271
8407
  @staticmethod