ripple-down-rules 0.1.66__tar.gz → 0.1.67__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.
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/PKG-INFO +1 -1
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/pyproject.toml +1 -1
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/datasets.py +1 -1
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/datastructures/callable_expression.py +17 -9
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/datastructures/dataclasses.py +1 -1
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/prompt.py +3 -9
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/rdr.py +1 -1
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/rules.py +14 -14
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/utils.py +53 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules.egg-info/PKG-INFO +1 -1
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/test/test_relational_rdr.py +5 -4
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/test/test_relational_rdr_alchemy.py +5 -4
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/LICENSE +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/README.md +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/setup.cfg +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/__init__.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/datastructures/__init__.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/datastructures/case.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/datastructures/enums.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/experts.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/failures.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/helpers.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/rdr_decorators.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules.egg-info/SOURCES.txt +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/test/test_json_serialization.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/test/test_on_mutagenic.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/test/test_rdr.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/test/test_rdr_alchemy.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/test/test_rdr_world.py +0 -0
- {ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/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.
|
3
|
+
Version: 0.1.67
|
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.
|
9
|
+
version = "0.1.67"
|
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" }]
|
@@ -125,7 +125,7 @@ class HabitatTable(MappedAsDataclass, Base):
|
|
125
125
|
return hash(self.habitat)
|
126
126
|
|
127
127
|
def __str__(self):
|
128
|
-
return self.habitat.
|
128
|
+
return f"{HabitatTable.__name__}({Habitat.__name__}.{self.habitat.name})"
|
129
129
|
|
130
130
|
def __repr__(self):
|
131
131
|
return self.__str__()
|
@@ -3,11 +3,13 @@ from __future__ import annotations
|
|
3
3
|
import ast
|
4
4
|
import logging
|
5
5
|
from _ast import AST
|
6
|
+
from enum import Enum
|
6
7
|
|
7
8
|
from typing_extensions import Type, Optional, Any, List, Union, Tuple, Dict, Set
|
8
9
|
|
9
10
|
from .case import create_case, Case
|
10
|
-
from ..utils import SubclassJSONSerializer, get_full_class_name, get_type_from_string, conclusion_to_json, is_iterable
|
11
|
+
from ..utils import SubclassJSONSerializer, get_full_class_name, get_type_from_string, conclusion_to_json, is_iterable, \
|
12
|
+
build_user_input_from_conclusion, encapsulate_user_input
|
11
13
|
|
12
14
|
|
13
15
|
class VariableVisitor(ast.NodeVisitor):
|
@@ -88,6 +90,7 @@ class CallableExpression(SubclassJSONSerializer):
|
|
88
90
|
"""
|
89
91
|
A callable that is constructed from a string statement written by an expert.
|
90
92
|
"""
|
93
|
+
encapsulating_function: str = "def _get_value(case):"
|
91
94
|
|
92
95
|
def __init__(self, user_input: Optional[str] = None, conclusion_type: Optional[Tuple[Type]] = None,
|
93
96
|
expression_tree: Optional[AST] = None,
|
@@ -103,8 +106,10 @@ class CallableExpression(SubclassJSONSerializer):
|
|
103
106
|
"""
|
104
107
|
if user_input is None and conclusion is None:
|
105
108
|
raise ValueError("Either user_input or conclusion must be provided.")
|
109
|
+
if user_input is None:
|
110
|
+
user_input = build_user_input_from_conclusion(conclusion)
|
106
111
|
self.conclusion: Optional[Any] = conclusion
|
107
|
-
self.user_input: str = user_input
|
112
|
+
self.user_input: str = encapsulate_user_input(user_input, self.encapsulating_function)
|
108
113
|
if conclusion_type is not None:
|
109
114
|
if is_iterable(conclusion_type):
|
110
115
|
conclusion_type = tuple(conclusion_type)
|
@@ -112,12 +117,11 @@ class CallableExpression(SubclassJSONSerializer):
|
|
112
117
|
conclusion_type = (conclusion_type,)
|
113
118
|
self.conclusion_type = conclusion_type
|
114
119
|
self.scope: Optional[Dict[str, Any]] = scope if scope is not None else {}
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
self.visitor.visit(self.expression_tree)
|
120
|
+
self.scope = get_used_scope(self.user_input, self.scope)
|
121
|
+
self.expression_tree: AST = expression_tree if expression_tree else parse_string_to_expression(self.user_input)
|
122
|
+
self.code = compile_expression_to_code(self.expression_tree)
|
123
|
+
self.visitor = VariableVisitor()
|
124
|
+
self.visitor.visit(self.expression_tree)
|
121
125
|
|
122
126
|
def __call__(self, case: Any, **kwargs) -> Any:
|
123
127
|
try:
|
@@ -145,7 +149,11 @@ class CallableExpression(SubclassJSONSerializer):
|
|
145
149
|
"""
|
146
150
|
Combine this callable expression with another callable expression using the 'and' operator.
|
147
151
|
"""
|
148
|
-
|
152
|
+
cond1_user_input = self.user_input.replace(self.encapsulating_function, "def _cond1(case):")
|
153
|
+
cond2_user_input = other.user_input.replace(self.encapsulating_function, "def _cond2(case):")
|
154
|
+
new_user_input = (f"{cond1_user_input}\n"
|
155
|
+
f"{cond2_user_input}\n"
|
156
|
+
f"return _cond1(case) and _cond2(case)")
|
149
157
|
return CallableExpression(new_user_input, conclusion_type=self.conclusion_type)
|
150
158
|
|
151
159
|
def __eq__(self, other):
|
@@ -120,7 +120,7 @@ class CaseQuery:
|
|
120
120
|
"""
|
121
121
|
:return: The target expression of the attribute.
|
122
122
|
"""
|
123
|
-
if self._target is not None and not isinstance(self._target, CallableExpression):
|
123
|
+
if (self._target is not None) and (not isinstance(self._target, CallableExpression)):
|
124
124
|
self._target = CallableExpression(conclusion=self._target, conclusion_type=self.attribute_type,
|
125
125
|
scope=self.scope)
|
126
126
|
return self._target
|
@@ -80,16 +80,10 @@ class IPythonShell:
|
|
80
80
|
self.user_input = None
|
81
81
|
else:
|
82
82
|
self.all_code_lines = extract_dependencies(self.shell.all_lines)
|
83
|
-
if len(self.all_code_lines) == 1:
|
84
|
-
|
85
|
-
self.user_input = None
|
86
|
-
else:
|
87
|
-
self.user_input = self.all_code_lines[0].replace('return', '').strip()
|
83
|
+
if len(self.all_code_lines) == 1 and self.all_code_lines[0].strip() == '':
|
84
|
+
self.user_input = None
|
88
85
|
else:
|
89
|
-
self.user_input =
|
90
|
-
for cl in self.all_code_lines:
|
91
|
-
sub_code_lines = cl.split('\n')
|
92
|
-
self.user_input += '\n '.join(sub_code_lines) + '\n '
|
86
|
+
self.user_input = '\n'.join(self.all_code_lines)
|
93
87
|
|
94
88
|
|
95
89
|
def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor, prompt_str: Optional[str] = None)\
|
@@ -715,7 +715,7 @@ class GeneralRDR(RippleDownRules):
|
|
715
715
|
|
716
716
|
@staticmethod
|
717
717
|
def _classify(classifiers_dict: Dict[str, Union[ModuleType, RippleDownRules]],
|
718
|
-
case: Any, modify_original_case: bool =
|
718
|
+
case: Any, modify_original_case: bool = False) -> Dict[str, Any]:
|
719
719
|
"""
|
720
720
|
Classify a case by going through all classifiers and adding the categories that are classified,
|
721
721
|
and then restarting the classification until no more categories can be added.
|
@@ -270,11 +270,11 @@ class SingleClassRule(Rule, HasAlternativeRule, HasRefinementRule):
|
|
270
270
|
|
271
271
|
def _conclusion_source_code(self, conclusion: Any, parent_indent: str = "") -> Tuple[Optional[str], str]:
|
272
272
|
conclusion = str(conclusion)
|
273
|
-
indent = parent_indent + " " * 4
|
274
|
-
if '\n' not in conclusion:
|
275
|
-
|
276
|
-
else:
|
277
|
-
|
273
|
+
# indent = parent_indent + " " * 4
|
274
|
+
# if '\n' not in conclusion:
|
275
|
+
# return None, f"{indent}return {conclusion}\n"
|
276
|
+
# else:
|
277
|
+
return get_rule_conclusion_as_source_code(self, conclusion, parent_indent=parent_indent)
|
278
278
|
|
279
279
|
def _if_statement_source_code_clause(self) -> str:
|
280
280
|
return "elif" if self.weight == RDREdge.Alternative.value else "if"
|
@@ -367,15 +367,15 @@ class MultiClassTopRule(Rule, HasRefinementRule, HasAlternativeRule):
|
|
367
367
|
def _conclusion_source_code(self, conclusion: Any, parent_indent: str = "") -> Tuple[str, str]:
|
368
368
|
conclusion_str = str(conclusion)
|
369
369
|
indent = parent_indent + " " * 4
|
370
|
-
if '\n' not in conclusion_str:
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
else:
|
377
|
-
|
378
|
-
|
370
|
+
# if '\n' not in conclusion_str:
|
371
|
+
# func = None
|
372
|
+
# if is_iterable(conclusion):
|
373
|
+
# conclusion_str = "{" + ", ".join([str(c) for c in conclusion]) + "}"
|
374
|
+
# else:
|
375
|
+
# conclusion_str = "{" + str(conclusion) + "}"
|
376
|
+
# else:
|
377
|
+
func, func_call = get_rule_conclusion_as_source_code(self, conclusion_str, parent_indent=parent_indent)
|
378
|
+
conclusion_str = func_call.replace("return ", "").strip()
|
379
379
|
|
380
380
|
statement = f"{indent}conclusions.update(make_set({conclusion_str}))\n"
|
381
381
|
if self.alternative is None:
|
@@ -10,6 +10,7 @@ import re
|
|
10
10
|
from collections import UserDict
|
11
11
|
from copy import deepcopy
|
12
12
|
from dataclasses import is_dataclass, fields
|
13
|
+
from enum import Enum
|
13
14
|
from types import NoneType
|
14
15
|
|
15
16
|
import matplotlib
|
@@ -34,6 +35,58 @@ import ast
|
|
34
35
|
matplotlib.use("Qt5Agg") # or "Qt5Agg", depending on availability
|
35
36
|
|
36
37
|
|
38
|
+
def encapsulate_user_input(user_input: str, func_signature: str) -> str:
|
39
|
+
"""
|
40
|
+
Encapsulate the user input string with a function definition.
|
41
|
+
|
42
|
+
:param user_input: The user input string.
|
43
|
+
:param func_signature: The function signature to use for encapsulation.
|
44
|
+
:return: The encapsulated user input string.
|
45
|
+
"""
|
46
|
+
if func_signature not in user_input:
|
47
|
+
new_user_input = func_signature + "\n "
|
48
|
+
if "return " not in user_input:
|
49
|
+
if '\n' not in user_input:
|
50
|
+
new_user_input += f"return {user_input}"
|
51
|
+
else:
|
52
|
+
raise ValueError("User input must contain a return statement or be a single line.")
|
53
|
+
else:
|
54
|
+
for cl in user_input.split('\n'):
|
55
|
+
sub_code_lines = cl.split('\n')
|
56
|
+
new_user_input += '\n '.join(sub_code_lines) + '\n '
|
57
|
+
else:
|
58
|
+
new_user_input = user_input
|
59
|
+
return new_user_input
|
60
|
+
|
61
|
+
|
62
|
+
def build_user_input_from_conclusion(conclusion: Any) -> str:
|
63
|
+
"""
|
64
|
+
Build a user input string from the conclusion.
|
65
|
+
|
66
|
+
:param conclusion: The conclusion to use for the callable expression.
|
67
|
+
:return: The user input string.
|
68
|
+
"""
|
69
|
+
|
70
|
+
# set user_input to the string representation of the conclusion
|
71
|
+
if isinstance(conclusion, set):
|
72
|
+
user_input = '{' + f"{', '.join([conclusion_to_str(t) for t in conclusion])}" + '}'
|
73
|
+
elif isinstance(conclusion, list):
|
74
|
+
user_input = '[' + f"{', '.join([conclusion_to_str(t) for t in conclusion])}" + ']'
|
75
|
+
elif isinstance(conclusion, tuple):
|
76
|
+
user_input = '(' + f"{', '.join([conclusion_to_str(t) for t in conclusion])}" + ')'
|
77
|
+
else:
|
78
|
+
user_input = conclusion_to_str(conclusion)
|
79
|
+
|
80
|
+
return user_input
|
81
|
+
|
82
|
+
|
83
|
+
def conclusion_to_str(conclusion_: Any) -> str:
|
84
|
+
if isinstance(conclusion_, Enum):
|
85
|
+
return type(conclusion_).__name__ + '.' + conclusion_.name
|
86
|
+
else:
|
87
|
+
return str(conclusion_)
|
88
|
+
|
89
|
+
|
37
90
|
def update_case(case_query: CaseQuery, conclusions: Dict[str, Any]):
|
38
91
|
"""
|
39
92
|
Update the case with the conclusions.
|
{ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules.egg-info/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ripple_down_rules
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.67
|
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
|
@@ -54,6 +54,7 @@ class RelationalRDRTestCase(TestCase):
|
|
54
54
|
part_d: Part
|
55
55
|
part_e: Part
|
56
56
|
part_f: Part
|
57
|
+
target: List[PhysicalObject]
|
57
58
|
|
58
59
|
@classmethod
|
59
60
|
def setUpClass(cls):
|
@@ -71,8 +72,8 @@ class RelationalRDRTestCase(TestCase):
|
|
71
72
|
cls.part_d.contained_objects = [cls.part_e]
|
72
73
|
cls.part_e.contained_objects = [cls.part_f]
|
73
74
|
cls.robot: Robot = robot
|
74
|
-
cls.case_query = CaseQuery(robot, "contained_objects", (PhysicalObject,), False
|
75
|
-
|
75
|
+
cls.case_query = CaseQuery(robot, "contained_objects", (PhysicalObject,), False)
|
76
|
+
cls.target = [cls.part_b, cls.part_c, cls.part_d, cls.part_e]
|
76
77
|
|
77
78
|
def test_classify_scrdr(self):
|
78
79
|
use_loaded_answers = True
|
@@ -86,7 +87,7 @@ class RelationalRDRTestCase(TestCase):
|
|
86
87
|
cat = scrdr.fit_case(CaseQuery(self.robot, "contained_objects", (PhysicalObject,), False), expert=expert)
|
87
88
|
render_tree(scrdr.start_rule, use_dot_exporter=True,
|
88
89
|
filename=self.test_results_dir + "/relational_scrdr_classify")
|
89
|
-
assert cat == self.
|
90
|
+
assert cat == self.target
|
90
91
|
|
91
92
|
if save_answers:
|
92
93
|
cwd = os.getcwd()
|
@@ -105,4 +106,4 @@ class RelationalRDRTestCase(TestCase):
|
|
105
106
|
conclusion = CallableExpression(user_input, list)
|
106
107
|
print(conclusion)
|
107
108
|
print(conclusion(self.robot))
|
108
|
-
assert conclusion(self.robot) == self.
|
109
|
+
assert conclusion(self.robot) == self.target
|
@@ -108,6 +108,7 @@ class RelationalRDRTestCase(TestCase):
|
|
108
108
|
part_f: PhysicalObject
|
109
109
|
rob_has_parts: List[HasPart]
|
110
110
|
containments: List[ContainsObject]
|
111
|
+
target: List[PhysicalObject]
|
111
112
|
|
112
113
|
@classmethod
|
113
114
|
def setUpClass(cls):
|
@@ -129,8 +130,8 @@ class RelationalRDRTestCase(TestCase):
|
|
129
130
|
cls.containments.append(ContainsObject(left=cls.part_d, right=cls.part_e))
|
130
131
|
cls.containments.append(ContainsObject(left=cls.part_e, right=cls.part_f))
|
131
132
|
cls.robot: PhysicalObject = robot
|
132
|
-
cls.case_query = CaseQuery(robot, robot.contained_objects, (PhysicalObject,), False
|
133
|
-
|
133
|
+
cls.case_query = CaseQuery(robot, robot.contained_objects, (PhysicalObject,), False)
|
134
|
+
cls.target = [cls.part_b, cls.part_c, cls.part_d, cls.part_e]
|
134
135
|
|
135
136
|
def test_setup(self):
|
136
137
|
assert self.robot.parts == [self.part_a, self.part_b, self.part_c, self.part_d]
|
@@ -153,7 +154,7 @@ class RelationalRDRTestCase(TestCase):
|
|
153
154
|
cat = scrdr.fit_case(CaseQuery(self.robot, "contained_objects", (PhysicalObject,), False), expert=expert)
|
154
155
|
render_tree(scrdr.start_rule, use_dot_exporter=True,
|
155
156
|
filename=self.test_results_dir + "/relational_scrdr_classify")
|
156
|
-
assert cat == self.
|
157
|
+
assert cat == self.target
|
157
158
|
|
158
159
|
if save_answers:
|
159
160
|
cwd = os.getcwd()
|
@@ -172,4 +173,4 @@ class RelationalRDRTestCase(TestCase):
|
|
172
173
|
conclusion = CallableExpression(user_input, list)
|
173
174
|
print(conclusion)
|
174
175
|
print(conclusion(self.robot))
|
175
|
-
assert conclusion(self.robot) == self.
|
176
|
+
assert conclusion(self.robot) == self.target
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/datastructures/case.py
RENAMED
File without changes
|
{ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/datastructures/enums.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules/rdr_decorators.py
RENAMED
File without changes
|
{ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
{ripple_down_rules-0.1.66 → ripple_down_rules-0.1.67}/src/ripple_down_rules.egg-info/top_level.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|