ripple-down-rules 0.0.14__py3-none-any.whl → 0.0.15__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.
ripple_down_rules/rdr.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import importlib
4
4
  from abc import ABC, abstractmethod
5
- from copy import copy, deepcopy
5
+ from copy import copy
6
6
  from types import ModuleType
7
7
 
8
8
  from matplotlib import pyplot as plt
@@ -120,7 +120,8 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
120
120
  plt.show()
121
121
 
122
122
  @staticmethod
123
- def calculate_precision_and_recall(pred_cat: List[CaseAttribute], target: List[CaseAttribute]) -> Tuple[List[bool], List[bool]]:
123
+ def calculate_precision_and_recall(pred_cat: List[CaseAttribute], target: List[CaseAttribute]) -> Tuple[
124
+ List[bool], List[bool]]:
124
125
  """
125
126
  :param pred_cat: The predicted category.
126
127
  :param target: The target category.
@@ -194,16 +195,17 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
194
195
 
195
196
  :param file_path: The path to the file to write the source code to.
196
197
  """
197
- func_def = f"def classify(case: {self.case_type.__name__}) -> {self._get_conclusion_type_hint()}:\n"
198
+ func_def = f"def classify(case: {self.case_type.__name__}) -> {self.conclusion_type_hint}:\n"
198
199
  with open(file_path + f"/{self.generated_python_file_name}.py", "w") as f:
199
200
  f.write(self._get_imports() + "\n\n")
200
201
  f.write(func_def)
201
- f.write(f"{' '*4}if not isinstance(case, Case):\n"
202
- f"{' '*4} case = create_case(case, recursion_idx=3)\n""")
202
+ f.write(f"{' ' * 4}if not isinstance(case, Case):\n"
203
+ f"{' ' * 4} case = create_case(case, recursion_idx=3)\n""")
203
204
  self.write_rules_as_source_code_to_file(self.start_rule, f, " " * 4)
204
205
 
206
+ @property
205
207
  @abstractmethod
206
- def _get_conclusion_type_hint(self) -> str:
208
+ def conclusion_type_hint(self) -> str:
207
209
  """
208
210
  :return: The type hint of the conclusion of the rdr as a string.
209
211
  """
@@ -254,6 +256,8 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
254
256
  if isinstance(self.start_rule.conclusion, CallableExpression):
255
257
  return self.start_rule.conclusion.conclusion_type
256
258
  else:
259
+ if isinstance(self.start_rule.conclusion, set):
260
+ return type(list(self.start_rule.conclusion)[0])
257
261
  return type(self.start_rule.conclusion)
258
262
 
259
263
 
@@ -316,7 +320,8 @@ class SingleClassRDR(RDRWithCodeWriter):
316
320
  if rule.alternative:
317
321
  self.write_rules_as_source_code_to_file(rule.alternative, file, parent_indent)
318
322
 
319
- def _get_conclusion_type_hint(self) -> str:
323
+ @property
324
+ def conclusion_type_hint(self) -> str:
320
325
  return self.conclusion_type.__name__
321
326
 
322
327
  def _to_json(self) -> Dict[str, Any]:
@@ -427,6 +432,8 @@ class MultiClassRDR(RDRWithCodeWriter):
427
432
  file, parent_indent: str = ""):
428
433
  """
429
434
  Write the rules as source code to a file.
435
+
436
+ :
430
437
  """
431
438
  if rule == self.start_rule:
432
439
  file.write(f"{parent_indent}conclusions = set()\n")
@@ -435,14 +442,15 @@ class MultiClassRDR(RDRWithCodeWriter):
435
442
  conclusion_indent = parent_indent
436
443
  if hasattr(rule, "refinement") and rule.refinement:
437
444
  self.write_rules_as_source_code_to_file(rule.refinement, file, parent_indent + " ")
438
- conclusion_indent = parent_indent + " "*4
445
+ conclusion_indent = parent_indent + " " * 4
439
446
  file.write(f"{conclusion_indent}else:\n")
440
447
  file.write(rule.write_conclusion_as_source_code(conclusion_indent))
441
448
 
442
449
  if rule.alternative:
443
450
  self.write_rules_as_source_code_to_file(rule.alternative, file, parent_indent)
444
451
 
445
- def _get_conclusion_type_hint(self) -> str:
452
+ @property
453
+ def conclusion_type_hint(self) -> str:
446
454
  return f"Set[{self.conclusion_type.__name__}]"
447
455
 
448
456
  def _get_imports(self) -> str:
@@ -654,7 +662,7 @@ class GeneralRDR(RippleDownRules):
654
662
  @start_rule.setter
655
663
  def start_rule(self, value: Union[SingleClassRDR, MultiClassRDR]):
656
664
  if value:
657
- self.start_rules_dict[type(value.start_rule.conclusion)] = value
665
+ self.start_rules_dict[value.conclusion_type] = value
658
666
 
659
667
  @property
660
668
  def start_rules(self) -> List[Union[SingleClassRule, MultiClassTopRule]]:
@@ -665,6 +673,19 @@ class GeneralRDR(RippleDownRules):
665
673
  Classify a case by going through all RDRs and adding the categories that are classified, and then restarting
666
674
  the classification until no more categories can be added.
667
675
 
676
+ :param case: The case to classify.
677
+ :return: The categories that the case belongs to.
678
+ """
679
+ return self._classify(self.start_rules_dict, case)
680
+
681
+ @staticmethod
682
+ def _classify(classifiers_dict: Dict[Type, Union[ModuleType, RippleDownRules]],
683
+ case: Union[Case, SQLTable]) -> Optional[List[Any]]:
684
+ """
685
+ Classify a case by going through all classifiers and adding the categories that are classified,
686
+ and then restarting the classification until no more categories can be added.
687
+
688
+ :param classifiers_dict: A dictionary mapping conclusion types to the classifiers that produce them.
668
689
  :param case: The case to classify.
669
690
  :return: The categories that the case belongs to.
670
691
  """
@@ -672,8 +693,8 @@ class GeneralRDR(RippleDownRules):
672
693
  case_cp = copy_case(case)
673
694
  while True:
674
695
  added_attributes = False
675
- for cat_type, rdr in self.start_rules_dict.items():
676
- if self.case_has_conclusion(case_cp, cat_type):
696
+ for cat_type, rdr in classifiers_dict.items():
697
+ if GeneralRDR.case_has_conclusion(case_cp, cat_type):
677
698
  continue
678
699
  pred_atts = rdr.classify(case_cp)
679
700
  if pred_atts:
@@ -733,7 +754,7 @@ class GeneralRDR(RippleDownRules):
733
754
  conclusions = rdr.classify(case_cp)
734
755
  else:
735
756
  conclusions = self.start_rules_dict[target_type].fit_case(case_query_cp,
736
- expert, **kwargs)
757
+ expert, **kwargs)
737
758
  self.update_case(case_cp, conclusions, rdr_type)
738
759
 
739
760
  return self.classify(case)
@@ -815,3 +836,66 @@ class GeneralRDR(RippleDownRules):
815
836
  k = get_type_from_string(k)
816
837
  start_rules_dict[k] = get_type_from_string(v['_type']).from_json(v)
817
838
  return cls(start_rules_dict)
839
+
840
+ def write_to_python_file(self, file_path: str):
841
+ """
842
+ Write the tree of rules as source code to a file.
843
+
844
+ :param file_path: The path to the file to write the source code to.
845
+ """
846
+ for rdr in self.start_rules_dict.values():
847
+ rdr.write_to_python_file(file_path)
848
+ func_def = f"def classify(case: {self.case_type.__name__}) -> {self.conclusion_type_hint}:\n"
849
+ with open(file_path + f"/{self.generated_python_file_name}.py", "w") as f:
850
+ f.write(self._get_imports(file_path) + "\n\n")
851
+ f.write("classifiers_dict = dict()\n")
852
+ for t, rdr in self.start_rules_dict.items():
853
+ f.write(f"classifiers_dict[{t.__name__}] = {t.__name__.lower()}_classifier\n")
854
+ f.write("\n\n")
855
+ f.write(func_def)
856
+ f.write(f"{' ' * 4}if not isinstance(case, Case):\n"
857
+ f"{' ' * 4} case = create_case(case, recursion_idx=3)\n""")
858
+ f.write(f"{' ' * 4}return GeneralRDR._classify(classifiers_dict, case)\n")
859
+
860
+ @property
861
+ def case_type(self) -> Type:
862
+ """
863
+ :return: The type of the case (input) to the RDR classifier.
864
+ """
865
+ if isinstance(self.start_rule.corner_case, Case):
866
+ return self.start_rule.corner_case._type
867
+ else:
868
+ return type(self.start_rule.corner_case)
869
+
870
+ def get_rdr_classifier_from_python_file(self, file_path: str):
871
+ """
872
+ :param file_path: The path to the file that contains the RDR classifier function.
873
+ :return: The module that contains the rdr classifier function.
874
+ """
875
+ return importlib.import_module(f"{file_path.strip('./')}.{self.generated_python_file_name}").classify
876
+
877
+ @property
878
+ def generated_python_file_name(self) -> str:
879
+ return f"{self.case_type.__name__.lower()}_grdr"
880
+
881
+ @property
882
+ def conclusion_type_hint(self) -> str:
883
+ return f"List[Union[{', '.join([rdr.conclusion_type_hint for rdr in self.start_rules_dict.values()])}]]"
884
+
885
+ def _get_imports(self, file_path: str) -> str:
886
+ imports = ""
887
+ # add type hints
888
+ imports += f"from typing_extensions import List, Union, Set\n"
889
+ # import rdr type
890
+ imports += f"from ripple_down_rules.rdr import GeneralRDR\n"
891
+ # add case type
892
+ imports += f"from ripple_down_rules.datastructures import Case, create_case\n"
893
+ imports += f"from {self.case_type.__module__} import {self.case_type.__name__}\n"
894
+ # add conclusion type imports
895
+ for conclusion_type in self.start_rules_dict.keys():
896
+ imports += f"from {conclusion_type.__module__} import {conclusion_type.__name__}\n"
897
+ # add rdr python generated functions.
898
+ for conclusion_type, rdr in self.start_rules_dict.items():
899
+ imports += (f"from {file_path.strip('./')}"
900
+ f" import {rdr.generated_python_file_name} as {conclusion_type.__name__.lower()}_classifier\n")
901
+ return imports
@@ -334,7 +334,11 @@ class MultiClassTopRule(Rule, HasRefinementRule, HasAlternativeRule):
334
334
  return loaded_rule
335
335
 
336
336
  def _conclusion_source_code_clause(self, conclusion: Any, parent_indent: str = "") -> str:
337
- statement = f"{parent_indent}{' ' * 4}conclusions.add({conclusion})\n"
337
+ if is_iterable(conclusion):
338
+ conclusion_str = "{" + ", ".join([str(c) for c in conclusion]) + "}"
339
+ else:
340
+ conclusion_str = "{" + str(conclusion) + "}"
341
+ statement = f"{parent_indent}{' ' * 4}conclusions.update({conclusion_str})\n"
338
342
  if self.alternative is None:
339
343
  statement += f"{parent_indent}return conclusions\n"
340
344
  return statement
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.0.14
3
+ Version: 0.0.15
4
4
  Summary: Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning.
5
5
  Author-email: Abdelrhman Bassiouny <abassiou@uni-bremen.de>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -3,16 +3,16 @@ ripple_down_rules/datasets.py,sha256=qYX7IF7ACm0VRbaKfEgQ32j0YbUUyt2GfGU5Lo42CqI
3
3
  ripple_down_rules/experts.py,sha256=wg1uY0ox9dUMR4s1RdGjzpX1_WUqnCa060r1U9lrKYI,11214
4
4
  ripple_down_rules/failures.py,sha256=E6ajDUsw3Blom8eVLbA7d_Qnov2conhtZ0UmpQ9ZtSE,302
5
5
  ripple_down_rules/prompt.py,sha256=QAmxg4ssrGUAlK7lbyKw2nuRczTZColpjc9uMC1ts3I,4210
6
- ripple_down_rules/rdr.py,sha256=Z-Zu8dH1kdlwOzTVT3DrDdqkMXk6xFQze-Mtz1YZ320,38142
7
- ripple_down_rules/rules.py,sha256=Gi_GRYDvnwxPIHEh_aP1NmN9cL2pHZbwbaiO6M7YdU0,14044
6
+ ripple_down_rules/rdr.py,sha256=am57_cmN2ge_eKGQLIAyd5iH4iSC15QrFHmdmwwM_uY,41947
7
+ ripple_down_rules/rules.py,sha256=MUZv42WPOB73rzw6Um2F_q0woRFG4db4yYB6R-qMpKA,14239
8
8
  ripple_down_rules/utils.py,sha256=32gU9NHIcxQpfS4zPSAtn63np5SdVvS9VuyoYiyKZbc,18664
9
9
  ripple_down_rules/datastructures/__init__.py,sha256=zpmiYm4WkwNHaGdTIfacS7llN5d2xyU6U-saH_TpydI,103
10
10
  ripple_down_rules/datastructures/callable_expression.py,sha256=TN6bi4VYjyLlSLTEA3dRo5ENfEdQYc8Fjj5nbnsz-C0,9058
11
11
  ripple_down_rules/datastructures/case.py,sha256=0EV69VBzsBTHQ3Ots7fkP09g1tyJ20xW9kSm0nZGy40,14071
12
12
  ripple_down_rules/datastructures/dataclasses.py,sha256=EVQ1jBKW7K7q7_JNgikHX9fm3EmQQKA74sNjEQ4rXn8,2449
13
13
  ripple_down_rules/datastructures/enums.py,sha256=l0Eu-TeJ6qB2XHoJycXmUgLw-3yUebQ8SsEbW8bBZdM,4543
14
- ripple_down_rules-0.0.14.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
15
- ripple_down_rules-0.0.14.dist-info/METADATA,sha256=VHKBkYFWhQvidy3vV2Td6kyLLjrwkYMv18ROs_dbb2o,42519
16
- ripple_down_rules-0.0.14.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
17
- ripple_down_rules-0.0.14.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
18
- ripple_down_rules-0.0.14.dist-info/RECORD,,
14
+ ripple_down_rules-0.0.15.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
15
+ ripple_down_rules-0.0.15.dist-info/METADATA,sha256=JOCvhnY9h7adkNGnR9uX0j_d7oQcIIBTY8kCpZ2VsD4,42519
16
+ ripple_down_rules-0.0.15.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
17
+ ripple_down_rules-0.0.15.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
18
+ ripple_down_rules-0.0.15.dist-info/RECORD,,