ripple-down-rules 0.1.0__tar.gz → 0.1.2__tar.gz

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 (31) hide show
  1. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/PKG-INFO +1 -1
  2. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/pyproject.toml +1 -1
  3. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/datastructures/case.py +1 -0
  4. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/rdr.py +12 -16
  5. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/utils.py +16 -3
  6. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules.egg-info/PKG-INFO +1 -1
  7. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/test/test_on_mutagenic.py +12 -2
  8. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/LICENSE +0 -0
  9. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/README.md +0 -0
  10. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/setup.cfg +0 -0
  11. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/__init__.py +0 -0
  12. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/datasets.py +0 -0
  13. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/datastructures/__init__.py +0 -0
  14. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/datastructures/callable_expression.py +0 -0
  15. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/datastructures/dataclasses.py +0 -0
  16. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/datastructures/enums.py +0 -0
  17. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/experts.py +0 -0
  18. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/failures.py +0 -0
  19. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/helpers.py +0 -0
  20. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/prompt.py +0 -0
  21. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/rdr_decorators.py +0 -0
  22. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules/rules.py +0 -0
  23. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules.egg-info/SOURCES.txt +0 -0
  24. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
  25. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
  26. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/test/test_json_serialization.py +0 -0
  27. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/test/test_rdr.py +0 -0
  28. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/test/test_rdr_alchemy.py +0 -0
  29. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/test/test_relational_rdr.py +0 -0
  30. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/test/test_relational_rdr_alchemy.py +0 -0
  31. {ripple_down_rules-0.1.0 → ripple_down_rules-0.1.2}/test/test_sql_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.1.0
3
+ Version: 0.1.2
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
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "ripple_down_rules"
9
- version = "0.1.0"
9
+ version = "0.1.2"
10
10
  description = "Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning."
11
11
  readme = "README.md"
12
12
  authors = [{ name = "Abdelrhman Bassiouny", email = "abassiou@uni-bremen.de" }]
@@ -214,6 +214,7 @@ def create_case(obj: Any, recursion_idx: int = 0, max_recursion_idx: int = 0,
214
214
  :param parent_is_iterable: Boolean indicating whether the parent object is iterable or not.
215
215
  :return: The case that represents the object.
216
216
  """
217
+ obj_name = obj_name or obj.__class__.__name__
217
218
  if isinstance(obj, DataFrame):
218
219
  return create_cases_from_dataframe(obj, obj_name)
219
220
  if isinstance(obj, Case):
@@ -132,12 +132,7 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
132
132
  for pred_key, pred_value in pred_cat.items():
133
133
  if pred_key not in target:
134
134
  continue
135
- # if is_iterable(pred_value):
136
- # print(pred_value, target[pred_key])
137
- # precision.extend([v in make_set(target[pred_key]) for v in make_set(pred_value)])
138
135
  precision.extend([v in make_set(target[pred_key]) for v in make_set(pred_value)])
139
- # else:
140
- # precision.append(pred_value == target[pred_key])
141
136
  for target_key, target_value in target.items():
142
137
  if target_key not in pred_cat:
143
138
  recall.append(False)
@@ -146,7 +141,6 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
146
141
  recall.extend([v in pred_cat[target_key] for v in target_value])
147
142
  else:
148
143
  recall.append(target_value == pred_cat[target_key])
149
- print(f"Precision: {precision}, Recall: {recall}")
150
144
  else:
151
145
  if isinstance(target, dict):
152
146
  target = list(target.values())
@@ -169,9 +163,9 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
169
163
  Update the figures of the classifier.
170
164
  """
171
165
  if isinstance(self, GeneralRDR):
172
- for i, (_type, rdr) in enumerate(self.start_rules_dict.items()):
166
+ for i, (rdr_name, rdr) in enumerate(self.start_rules_dict.items()):
173
167
  if not rdr.fig:
174
- rdr.fig = plt.figure(f"Rule {i}: {_type.__name__}")
168
+ rdr.fig = plt.figure(f"Rule {i}: {rdr_name}")
175
169
  draw_tree(rdr.start_rule, rdr.fig)
176
170
  else:
177
171
  if not self.fig:
@@ -214,7 +208,7 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
214
208
  f.write(self._get_imports() + "\n\n")
215
209
  f.write(func_def)
216
210
  f.write(f"{' ' * 4}if not isinstance(case, Case):\n"
217
- f"{' ' * 4} case = create_case(case, recursion_idx=3)\n""")
211
+ f"{' ' * 4} case = create_case(case, max_recursion_idx=3)\n""")
218
212
  self.write_rules_as_source_code_to_file(self.start_rule, f, " " * 4)
219
213
 
220
214
  @property
@@ -235,6 +229,11 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
235
229
  if self.conclusion_type.__module__ != "builtins":
236
230
  imports += f"from {self.conclusion_type.__module__} import {self.conclusion_type.__name__}\n"
237
231
  imports += "from ripple_down_rules.datastructures import Case, create_case\n"
232
+ for rule in [self.start_rule] + list(self.start_rule.descendants):
233
+ if rule.conditions:
234
+ if rule.conditions.scope is not None and len(rule.conditions.scope) > 0:
235
+ for k, v in rule.conditions.scope.items():
236
+ imports += f"from {v.__module__} import {v.__name__}\n"
238
237
  return imports
239
238
 
240
239
  def get_rdr_classifier_from_python_file(self, package_name) -> Callable[[Any], Any]:
@@ -246,11 +245,7 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
246
245
 
247
246
  @property
248
247
  def generated_python_file_name(self) -> str:
249
- return f"{self.conclusion_type.__name__.lower()}_{self.__class__.__name__}"
250
-
251
- @property
252
- def python_file_name(self):
253
- return f"{self.start_rule.conclusion.__name__.lower()}_rdr"
248
+ return f"{self.start_rule.corner_case._name.lower()}_{self.attribute_name}_rdr"
254
249
 
255
250
  @property
256
251
  def case_type(self) -> Type:
@@ -485,6 +480,7 @@ class MultiClassRDR(RDRWithCodeWriter):
485
480
  self.start_rule.conditions = conditions
486
481
  self.start_rule.conclusion = case_query.target
487
482
  self.start_rule.corner_case = case_query.case
483
+ self.start_rule.conclusion_name = case_query.attribute_name
488
484
 
489
485
  @property
490
486
  def last_top_rule(self) -> Optional[MultiClassTopRule]:
@@ -866,7 +862,7 @@ class GeneralRDR(RippleDownRules):
866
862
  f.write("\n\n")
867
863
  f.write(func_def)
868
864
  f.write(f"{' ' * 4}if not isinstance(case, Case):\n"
869
- f"{' ' * 4} case = create_case(case, recursion_idx=3)\n""")
865
+ f"{' ' * 4} case = create_case(case, max_recursion_idx=3)\n""")
870
866
  f.write(f"{' ' * 4}return GeneralRDR._classify(classifiers_dict, case)\n")
871
867
 
872
868
  @property
@@ -888,7 +884,7 @@ class GeneralRDR(RippleDownRules):
888
884
 
889
885
  @property
890
886
  def generated_python_file_name(self) -> str:
891
- return f"{self.case_type.__name__.lower()}_grdr"
887
+ return f"{self.start_rule.corner_case._name.lower()}_rdr"
892
888
 
893
889
  @property
894
890
  def conclusion_type_hint(self) -> str:
@@ -27,7 +27,14 @@ if TYPE_CHECKING:
27
27
  matplotlib.use("Qt5Agg") # or "Qt5Agg", depending on availability
28
28
 
29
29
 
30
- def serialize_dataclass(obj: Any) -> Dict:
30
+ def serialize_dataclass(obj: Any) -> Union[Dict, Any]:
31
+ """
32
+ Recursively serialize a dataclass to a dictionary. If the dataclass contains any nested dataclasses, they will be
33
+ serialized as well. If the object is not a dataclass, it will be returned as is.
34
+
35
+ :param obj: The dataclass to serialize.
36
+ :return: The serialized dataclass as a dictionary or the object itself if it is not a dataclass.
37
+ """
31
38
 
32
39
  def recursive_convert(obj):
33
40
  if is_dataclass(obj):
@@ -39,8 +46,6 @@ def serialize_dataclass(obj: Any) -> Dict:
39
46
  return [recursive_convert(item) for item in obj]
40
47
  elif isinstance(obj, dict):
41
48
  return {k: recursive_convert(v) for k, v in obj.items()}
42
- elif hasattr(obj, "__module__") and hasattr(obj, "__name__"):
43
- return {'_type': get_full_class_name(obj.__class__)}
44
49
  else:
45
50
  return obj
46
51
 
@@ -48,6 +53,14 @@ def serialize_dataclass(obj: Any) -> Dict:
48
53
 
49
54
 
50
55
  def deserialize_dataclass(data: dict) -> Any:
56
+ """
57
+ Recursively deserialize a dataclass from a dictionary, if the dictionary contains a key "__dataclass__" (Most likely
58
+ created by the serialize_dataclass function), it will be treated as a dataclass and deserialized accordingly,
59
+ otherwise it will be returned as is.
60
+
61
+ :param data: The dictionary to deserialize.
62
+ :return: The deserialized dataclass.
63
+ """
51
64
  def recursive_load(obj):
52
65
  if isinstance(obj, dict) and "__dataclass__" in obj:
53
66
  module_name, class_name = obj["__dataclass__"].rsplit(".", 1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.1.0
3
+ Version: 0.1.2
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
@@ -166,7 +166,7 @@ def get_two_molecules_model(draw_tree=False, load_answers=True, save_answers=Fal
166
166
  CaseQuery(molecule_2, attribute_name="mutagenic", target=molecule_2.mutagenic), ]
167
167
 
168
168
  rdr = SingleClassRDR()
169
- rdr.fit(case_queries, expert=expert, draw_tree=draw_tree)
169
+ rdr.fit(case_queries, expert=expert, animate_tree=draw_tree)
170
170
 
171
171
  for case_query in case_queries:
172
172
  r = rdr.classify(case_query.case)
@@ -196,5 +196,15 @@ def test_serialize_two_molecules_model():
196
196
  assert rdr.classify(make_molecule_2()) == loaded_rdr.classify(make_molecule_2())
197
197
 
198
198
 
199
+ def test_write_two_molecules_model_to_python():
200
+ rdr = get_two_molecules_model()
201
+ filename = "./test_generated_rdrs"
202
+ rdr.write_to_python_file(filename)
203
+ loaded_rdr = rdr.get_rdr_classifier_from_python_file(filename)
204
+ assert rdr.classify(make_molecule_1()) == loaded_rdr(make_molecule_1())
205
+ assert rdr.classify(make_molecule_2()) == loaded_rdr(make_molecule_2())
206
+
207
+
199
208
  if __name__ == '__main__':
200
- test_two_molecules()
209
+ # test_two_molecules()
210
+ test_write_two_molecules_model_to_python()