ripple-down-rules 0.6.23__tar.gz → 0.6.25__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.6.23 → ripple_down_rules-0.6.25}/PKG-INFO +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/scripts/live_dot_server_client.py +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/__init__.py +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/callable_expression.py +3 -2
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/dataclasses.py +2 -2
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/enums.py +4 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/experts.py +2 -2
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/helpers.py +28 -7
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/rdr.py +113 -34
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/rdr_decorators.py +7 -6
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/rules.py +160 -56
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/prompt.py +6 -3
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/utils.py +30 -15
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/PKG-INFO +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_json_serialization.py +8 -3
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_is_a_robot/rdr_metadata/datasets_physical_object_is_a_robot.json +5 -5
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/physical_object_select_objects_that_are_parts_of_robot_output__mcrdr.py +2 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/rdr_metadata/datasets_physical_object_select_objects_that_are_parts_of_robot.json +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/test_qt_gui_inline.py +3 -2
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.github/workflows/build_and_deploy_doc.yml +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.github/workflows/ci.yml +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.github/workflows/publish-to-test-pypi.yml +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.gitignore +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.idea/shelf/Uncommitted_changes_before_Checkout_at_2_4_25,_6_32_PM_[Changes]/shelved.patch +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.idea/shelf/Uncommitted_changes_before_Checkout_at_2_4_25,_6_32_PM_[Changes]1/shelved.patch +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/LICENSE +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/README.md +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/_config.yml +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/_toc.yml +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/bibliography.md +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/intro.md +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/references.bib +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/requirements.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/animal_species.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/rdr_metadata/part_containment_rdr.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/robot_contained_objects_mcrdr.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/robot_contained_objects_mcrdr_defs.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/robot_rdr.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/relational_example.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/images/scrdr.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/images/scrdr.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/images/thinking_pr2.jpg +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/pyproject.toml +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/pytest.ini +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements-dev-ci.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements-dev.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements-gui.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements-viz.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_extra.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_extra.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_stop_only.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_stop_only.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_stop_plus_rule.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_stop_plus_rule.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr_2.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr_2.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr_3.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr_3.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/grdr_Habitat.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/grdr_Habitat.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/grdr_Species.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/grdr_Species.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_extra.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_extra.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_extra_classify.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_extra_classify.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_stop_plus_rule_combined.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_stop_plus_rule_combined.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/partial_mcrdr_extra.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/partial_mcrdr_extra.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/relational_scrdr_classify.dot +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/relational_scrdr_classify.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/setup.cfg +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/case.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/start-code-server.sh +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/gui.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/ipython_custom_shell.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/object_diagram.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/template_file_creator.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/SOURCES.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/requires.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conf/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conf/world/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conf/world/base_config.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conf/world/handles_and_containers.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conftest.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/datasets.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/factories/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/factories/world/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/factories/world/handles_and_containers.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/correct_drawer_rdr_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/drawer_cabinet_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/grdr_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/grdr_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/grdr_expert_answers_fit_extra.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/grdr_expert_answers_fit_no_targets.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_expert_answers_fit_no_targets.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_expert_answers_stop_only_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_extra_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_extra_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_multi_line_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_stop_only_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_stop_plus_rule_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_stop_plus_rule_combined_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_stop_plus_rule_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mutagenic_expert_answers.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/relational_scrdr_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/scrdr_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/scrdr_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/scrdr_expert_answers_fit_no_targets.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/scrdr_multi_line_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/scrdr_world_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_generated_rdrs/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_helpers/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_helpers/helpers.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_helpers/object_diagram_case_query.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_helpers/object_diagram_person.png +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_object_diagram.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_on_mutagenic.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_alchemy.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_decorators.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_helpers_rdrs.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_world/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_world/conftest.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_world/test_rdr_world.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_relational_rdr.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_relational_rdr_alchemy.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_is_a_robot/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_output__scrdr.py +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_output__scrdr_defs.py +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_rdr.py +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/physical_object_select_objects_that_are_parts_of_robot_output__mcrdr_defs.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/physical_object_select_objects_that_are_parts_of_robot_rdr.py +1 -1
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_sql_model.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_template_file_creator.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/__init__.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/test_ipython.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/test_ipython_copilot.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/test_prompt.py +0 -0
- {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ripple_down_rules
|
3
|
-
Version: 0.6.
|
3
|
+
Version: 0.6.25
|
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
|
@@ -95,7 +95,7 @@ class CallableExpression(SubclassJSONSerializer):
|
|
95
95
|
encapsulating_function_name: str = "_get_value"
|
96
96
|
|
97
97
|
def __init__(self, user_input: Optional[str] = None,
|
98
|
-
conclusion_type: Optional[Tuple[Type]] = None,
|
98
|
+
conclusion_type: Optional[Tuple[Type, ...]] = None,
|
99
99
|
expression_tree: Optional[AST] = None,
|
100
100
|
scope: Optional[Dict[str, Any]] = None,
|
101
101
|
conclusion: Optional[Any] = None,
|
@@ -176,7 +176,8 @@ class CallableExpression(SubclassJSONSerializer):
|
|
176
176
|
new_user_input = (f"{cond1_user_input}\n"
|
177
177
|
f"{cond2_user_input}\n"
|
178
178
|
f"return _cond1(case) and _cond2(case)")
|
179
|
-
return CallableExpression(new_user_input, conclusion_type=self.conclusion_type
|
179
|
+
return CallableExpression(new_user_input, conclusion_type=self.conclusion_type,
|
180
|
+
mutually_exclusive=self.mutually_exclusive)
|
180
181
|
|
181
182
|
def update_user_input_from_file(self, file_path: str, function_name: str):
|
182
183
|
"""
|
@@ -31,7 +31,7 @@ class CaseQuery:
|
|
31
31
|
"""
|
32
32
|
The name of the attribute.
|
33
33
|
"""
|
34
|
-
_attribute_types: Tuple[Type]
|
34
|
+
_attribute_types: Tuple[Type, ...]
|
35
35
|
"""
|
36
36
|
The type(s) of the attribute.
|
37
37
|
"""
|
@@ -139,7 +139,7 @@ class CaseQuery:
|
|
139
139
|
attribute_types_str = f"Union[{', '.join([t.__name__ for t in self.core_attribute_type])}]"
|
140
140
|
else:
|
141
141
|
attribute_types_str = self.core_attribute_type[0].__name__
|
142
|
-
if
|
142
|
+
if not self.mutually_exclusive:
|
143
143
|
return f"List[{attribute_types_str}]"
|
144
144
|
else:
|
145
145
|
return attribute_types_str
|
{ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/enums.py
RENAMED
@@ -196,7 +196,10 @@ class RDREdge(Enum):
|
|
196
196
|
"""
|
197
197
|
Next edge, the edge that represents the next rule to be evaluated.
|
198
198
|
"""
|
199
|
-
|
199
|
+
Filter = "filter if"
|
200
|
+
"""
|
201
|
+
Filter edge, the edge that represents the filter condition.
|
202
|
+
"""
|
200
203
|
|
201
204
|
class ValueType(Enum):
|
202
205
|
Unary = auto()
|
@@ -41,14 +41,14 @@ class Expert(ABC):
|
|
41
41
|
A flag to indicate if the expert should use loaded answers or not.
|
42
42
|
"""
|
43
43
|
|
44
|
-
def __init__(self, use_loaded_answers: bool =
|
44
|
+
def __init__(self, use_loaded_answers: bool = False,
|
45
45
|
append: bool = False,
|
46
46
|
answers_save_path: Optional[str] = None):
|
47
47
|
self.all_expert_answers = []
|
48
48
|
self.use_loaded_answers = use_loaded_answers
|
49
49
|
self.append = append
|
50
50
|
self.answers_save_path = answers_save_path
|
51
|
-
if answers_save_path is not None:
|
51
|
+
if answers_save_path is not None and os.path.exists(answers_save_path + '.py'):
|
52
52
|
if use_loaded_answers:
|
53
53
|
self.load_answers(answers_save_path)
|
54
54
|
else:
|
@@ -2,15 +2,14 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import os
|
4
4
|
from types import ModuleType
|
5
|
+
from typing import Tuple
|
5
6
|
|
6
|
-
from ripple_down_rules.datastructures.dataclasses import CaseFactoryMetaData
|
7
|
-
|
8
|
-
from .datastructures.case import create_case
|
9
|
-
from .datastructures.dataclasses import CaseQuery
|
10
7
|
from typing_extensions import Type, Optional, Callable, Any, Dict, TYPE_CHECKING, Union
|
11
8
|
|
12
|
-
from .
|
9
|
+
from .datastructures.case import create_case, Case
|
10
|
+
from .datastructures.dataclasses import CaseQuery
|
13
11
|
from .utils import calculate_precision_and_recall
|
12
|
+
from .utils import get_func_rdr_model_name, copy_case, make_set, update_case
|
14
13
|
|
15
14
|
if TYPE_CHECKING:
|
16
15
|
from .rdr import RippleDownRules
|
@@ -55,12 +54,14 @@ def general_rdr_classify(classifiers_dict: Dict[str, Union[ModuleType, RippleDow
|
|
55
54
|
if attribute_name in new_conclusions:
|
56
55
|
temp_case_query = CaseQuery(case_cp, attribute_name, rdr.conclusion_type, rdr.mutually_exclusive)
|
57
56
|
update_case(temp_case_query, new_conclusions)
|
58
|
-
if len(new_conclusions) == 0 or len(classifiers_dict) == 1 and list(classifiers_dict.values())[
|
57
|
+
if len(new_conclusions) == 0 or len(classifiers_dict) == 1 and list(classifiers_dict.values())[
|
58
|
+
0].mutually_exclusive:
|
59
59
|
break
|
60
60
|
return conclusions
|
61
61
|
|
62
62
|
|
63
|
-
def is_matching(classifier: Callable[[Any], Any], case_query: CaseQuery,
|
63
|
+
def is_matching(classifier: Callable[[Any], Any], case_query: CaseQuery,
|
64
|
+
pred_cat: Optional[Dict[str, Any]] = None) -> bool:
|
64
65
|
"""
|
65
66
|
:param classifier: The RDR classifier to check the prediction of.
|
66
67
|
:param case_query: The case query to check.
|
@@ -95,3 +96,23 @@ def load_or_create_func_rdr_model(func, model_dir: str, rdr_type: Type[RippleDow
|
|
95
96
|
else:
|
96
97
|
rdr = rdr_type(**rdr_kwargs)
|
97
98
|
return rdr
|
99
|
+
|
100
|
+
|
101
|
+
def get_an_updated_case_copy(case: Case, conclusion: Callable, attribute_name: str, conclusion_type: Tuple[Type, ...],
|
102
|
+
mutually_exclusive: bool) -> Case:
|
103
|
+
"""
|
104
|
+
:param case: The case to copy and update.
|
105
|
+
:param conclusion: The conclusion to add to the case.
|
106
|
+
:param attribute_name: The name of the attribute to update.
|
107
|
+
:param conclusion_type: The type of the conclusion to update.
|
108
|
+
:param mutually_exclusive: Whether the rule belongs to a mutually exclusive RDR.
|
109
|
+
:return: A copy of the case updated with the given conclusion.
|
110
|
+
"""
|
111
|
+
case_cp = copy_case(case)
|
112
|
+
temp_case_query = CaseQuery(case_cp, attribute_name, conclusion_type,
|
113
|
+
mutually_exclusive=mutually_exclusive)
|
114
|
+
output = conclusion(case_cp)
|
115
|
+
if not isinstance(output, Dict):
|
116
|
+
output = {attribute_name: output}
|
117
|
+
update_case(temp_case_query, output)
|
118
|
+
return case_cp
|
@@ -1,9 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import importlib
|
4
|
+
import json
|
4
5
|
import os
|
5
6
|
from abc import ABC, abstractmethod
|
6
7
|
from copy import copy
|
8
|
+
from dataclasses import is_dataclass
|
7
9
|
from types import NoneType
|
8
10
|
|
9
11
|
from ripple_down_rules.datastructures.dataclasses import CaseFactoryMetaData
|
@@ -27,15 +29,16 @@ from .datastructures.case import Case, CaseAttribute, create_case
|
|
27
29
|
from .datastructures.dataclasses import CaseQuery
|
28
30
|
from .datastructures.enums import MCRDRMode
|
29
31
|
from .experts import Expert, Human
|
30
|
-
from .helpers import is_matching, general_rdr_classify
|
31
|
-
from .rules import Rule, SingleClassRule, MultiClassTopRule, MultiClassStopRule
|
32
|
+
from .helpers import is_matching, general_rdr_classify, get_an_updated_case_copy
|
33
|
+
from .rules import Rule, SingleClassRule, MultiClassTopRule, MultiClassStopRule, MultiClassRefinementRule, \
|
34
|
+
MultiClassFilterRule
|
32
35
|
|
33
36
|
try:
|
34
37
|
from .user_interface.gui import RDRCaseViewer
|
35
38
|
except ImportError as e:
|
36
39
|
RDRCaseViewer = None
|
37
40
|
from .utils import draw_tree, make_set, SubclassJSONSerializer, make_list, get_type_from_string, \
|
38
|
-
|
41
|
+
is_value_conflicting, extract_function_source, extract_imports, get_full_class_name, \
|
39
42
|
is_iterable, str_to_snake_case, get_import_path_from_path, get_imports_from_types, render_tree
|
40
43
|
|
41
44
|
|
@@ -97,11 +100,12 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
97
100
|
self.viewer = RDRCaseViewer.instances[0]
|
98
101
|
logger.error("No viewer was provided, but there is already an existing viewer. "
|
99
102
|
"Using the existing viewer.")
|
103
|
+
self.input_node: Optional[Rule] = None
|
100
104
|
|
101
105
|
@property
|
102
106
|
def viewer(self):
|
103
107
|
return self._viewer
|
104
|
-
|
108
|
+
|
105
109
|
@viewer.setter
|
106
110
|
def viewer(self, value):
|
107
111
|
self._viewer = value
|
@@ -110,23 +114,24 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
110
114
|
|
111
115
|
def render_evaluated_rule_tree(self, filename: str, show_full_tree: bool = False) -> None:
|
112
116
|
if show_full_tree:
|
113
|
-
|
117
|
+
start_rule = self.start_rule if self.input_node is None else self.input_node
|
118
|
+
render_tree(start_rule, use_dot_exporter=True, filename=filename)
|
114
119
|
else:
|
115
120
|
evaluated_rules = self.get_evaluated_rule_tree()
|
116
121
|
if evaluated_rules is not None and len(evaluated_rules) > 0:
|
117
122
|
render_tree(evaluated_rules[0], use_dot_exporter=True, filename=filename,
|
118
123
|
only_nodes=evaluated_rules)
|
119
124
|
|
120
|
-
def get_evaluated_rule_tree(self) -> List[Rule]:
|
125
|
+
def get_evaluated_rule_tree(self) -> Optional[List[Rule]]:
|
121
126
|
"""
|
122
127
|
Get the evaluated rule tree of the classifier.
|
123
128
|
|
124
129
|
:return: The evaluated rule tree.
|
125
130
|
"""
|
126
131
|
if self.start_rule is None:
|
127
|
-
return
|
128
|
-
|
129
|
-
evaluated_rule_tree = [r for r in [
|
132
|
+
return None
|
133
|
+
start_rule = self.start_rule
|
134
|
+
evaluated_rule_tree = [r for r in [start_rule] + list(start_rule.descendants) if r.evaluated]
|
130
135
|
return evaluated_rule_tree
|
131
136
|
|
132
137
|
def save(self, save_dir: Optional[str] = None, model_name: Optional[str] = None,
|
@@ -260,9 +265,38 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
260
265
|
def __call__(self, case: Union[Case, SQLTable]) -> Union[CallableExpression, Dict[str, CallableExpression]]:
|
261
266
|
return self.classify(case)
|
262
267
|
|
268
|
+
def classify(self, case: Union[Case, SQLTable], modify_case: bool = False, case_query: Optional[CaseQuery] = None) \
|
269
|
+
-> Optional[Union[CallableExpression, Dict[str, CallableExpression]]]:
|
270
|
+
"""
|
271
|
+
Classify a case using the RDR classifier.
|
272
|
+
|
273
|
+
:param case: The case to classify.
|
274
|
+
:param modify_case: Whether to modify the original case attributes with the conclusion or not.
|
275
|
+
:param case_query: The case query containing the case to classify and the target category to compare the case with.
|
276
|
+
:return: The category that the case belongs to.
|
277
|
+
"""
|
278
|
+
if self.start_rule is not None:
|
279
|
+
for rule in [self.start_rule] + list(self.start_rule.descendants):
|
280
|
+
rule.evaluated = False
|
281
|
+
rule.fired = False
|
282
|
+
if self.start_rule is not None and self.start_rule.parent is None:
|
283
|
+
if self.input_node is None:
|
284
|
+
self.input_node = type(self.start_rule)(parent=None, uid='0')
|
285
|
+
self.input_node.evaluated = False
|
286
|
+
self.input_node.fired = False
|
287
|
+
self.start_rule.parent = self.input_node
|
288
|
+
self.start_rule.weight = ""
|
289
|
+
if self.input_node is not None:
|
290
|
+
data = case.__dict__ if is_dataclass(case) else case
|
291
|
+
if hasattr(case, "items"):
|
292
|
+
self.input_node.name = json.dumps({k: str(v) for k, v in data.items()}, indent=4)
|
293
|
+
else:
|
294
|
+
self.input_node.name = str(data)
|
295
|
+
return self._classify(case, modify_case=modify_case, case_query=case_query)
|
296
|
+
|
263
297
|
@abstractmethod
|
264
|
-
def
|
265
|
-
|
298
|
+
def _classify(self, case: Union[Case, SQLTable], modify_case: bool = False,
|
299
|
+
case_query: Optional[CaseQuery] = None) \
|
266
300
|
-> Optional[Union[CallableExpression, Dict[str, CallableExpression]]]:
|
267
301
|
"""
|
268
302
|
Classify a case.
|
@@ -467,25 +501,51 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
|
|
467
501
|
conclusion_func_names = [f'conclusion_{rid}' for rid in rules_dict.keys()
|
468
502
|
if not isinstance(rules_dict[rid], MultiClassStopRule)]
|
469
503
|
all_func_names = condition_func_names + conclusion_func_names
|
504
|
+
rule_tree_file_path = f"{model_dir}/{self.generated_python_file_name}.py"
|
470
505
|
filepath = f"{model_dir}/{self.generated_python_defs_file_name}.py"
|
471
506
|
cases_path = f"{model_dir}/{self.generated_python_cases_file_name}.py"
|
472
507
|
cases_import_path = get_import_path_from_path(model_dir)
|
473
508
|
cases_import_path = f"{cases_import_path}.{self.generated_python_cases_file_name}" if cases_import_path \
|
474
509
|
else self.generated_python_cases_file_name
|
475
510
|
functions_source = extract_function_source(filepath, all_func_names, include_signature=False)
|
511
|
+
python_rule_tree_source = ""
|
512
|
+
with open(rule_tree_file_path, "r") as rule_tree_source:
|
513
|
+
python_rule_tree_source = rule_tree_source.read()
|
476
514
|
# get the scope from the imports in the file
|
477
515
|
scope = extract_imports(filepath, package_name=package_name)
|
516
|
+
rules_not_found = set()
|
478
517
|
for rule in [self.start_rule] + list(self.start_rule.descendants):
|
479
518
|
if rule.conditions is not None:
|
480
|
-
|
519
|
+
conditions_name = rule.generated_conditions_function_name
|
520
|
+
if conditions_name not in functions_source or conditions_name not in python_rule_tree_source:
|
521
|
+
rules_not_found.add(rule)
|
522
|
+
continue
|
523
|
+
rule.conditions.user_input = functions_source[conditions_name]
|
481
524
|
rule.conditions.scope = scope
|
482
525
|
if os.path.exists(cases_path):
|
483
526
|
module = importlib.import_module(cases_import_path, package=package_name)
|
484
527
|
importlib.reload(module)
|
485
528
|
rule.corner_case_metadata = module.__dict__.get(f"corner_case_{rule.uid}", None)
|
486
|
-
if
|
487
|
-
|
529
|
+
if not isinstance(rule, MultiClassStopRule):
|
530
|
+
conclusion_name = rule.generated_conclusion_function_name
|
531
|
+
if conclusion_name not in functions_source or conclusion_name not in python_rule_tree_source:
|
532
|
+
rules_not_found.add(rule)
|
533
|
+
rule.conclusion.user_input = functions_source[conclusion_name]
|
488
534
|
rule.conclusion.scope = scope
|
535
|
+
for rule in rules_not_found:
|
536
|
+
if isinstance(rule, MultiClassTopRule):
|
537
|
+
import pdb; pdb.set_trace()
|
538
|
+
rule.parent.set_immediate_alternative(rule.alternative)
|
539
|
+
if rule.refinement is not None:
|
540
|
+
ref_rules = [ref_rule for ref_rule in [rule.refinement] + list(rule.refinement.descendants)]
|
541
|
+
for ref_rule in ref_rules:
|
542
|
+
del ref_rule
|
543
|
+
else:
|
544
|
+
rule.parent.refinement = rule.alternative
|
545
|
+
if rule.alternative is not None:
|
546
|
+
rule.alternative = None
|
547
|
+
rule.parent = None
|
548
|
+
del rule
|
489
549
|
|
490
550
|
@abstractmethod
|
491
551
|
def write_rules_as_source_code_to_file(self, rule: Rule, file, parent_indent: str = "",
|
@@ -564,7 +624,7 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
|
|
564
624
|
"""
|
565
625
|
pass
|
566
626
|
|
567
|
-
def _get_types_to_import(self) -> Tuple[Set[Type], Set[Type], Set[Type]]:
|
627
|
+
def _get_types_to_import(self) -> Tuple[Set[Union[Type, Callable]], Set[Type], Set[Type]]:
|
568
628
|
"""
|
569
629
|
:return: The types of the main, defs, and corner cases files of the RDR classifier that will be imported.
|
570
630
|
"""
|
@@ -703,8 +763,8 @@ class SingleClassRDR(RDRWithCodeWriter):
|
|
703
763
|
expert.ask_for_conditions(case_query)
|
704
764
|
self.start_rule = SingleClassRule.from_case_query(case_query)
|
705
765
|
|
706
|
-
def
|
707
|
-
|
766
|
+
def _classify(self, case: Case, modify_case: bool = False,
|
767
|
+
case_query: Optional[CaseQuery] = None) -> Optional[Any]:
|
708
768
|
"""
|
709
769
|
Classify a case by recursively evaluating the rules until a rule fires or the last rule is reached.
|
710
770
|
|
@@ -818,8 +878,8 @@ class MultiClassRDR(RDRWithCodeWriter):
|
|
818
878
|
super(MultiClassRDR, self).__init__(start_rule, **kwargs)
|
819
879
|
self.mode: MCRDRMode = mode
|
820
880
|
|
821
|
-
def
|
822
|
-
|
881
|
+
def _classify(self, case: Union[Case, SQLTable], modify_case: bool = False,
|
882
|
+
case_query: Optional[CaseQuery] = None) -> Set[Any]:
|
823
883
|
evaluated_rule = self.start_rule
|
824
884
|
self.conclusions = []
|
825
885
|
while evaluated_rule:
|
@@ -897,6 +957,9 @@ class MultiClassRDR(RDRWithCodeWriter):
|
|
897
957
|
if rule.alternative:
|
898
958
|
self.write_rules_as_source_code_to_file(rule.alternative, filename, parent_indent, defs_file=defs_file,
|
899
959
|
cases_file=cases_file, package_name=package_name)
|
960
|
+
elif isinstance(rule, MultiClassTopRule):
|
961
|
+
with open(filename, "a") as file:
|
962
|
+
file.write(f"{parent_indent}return conclusions\n")
|
900
963
|
|
901
964
|
@property
|
902
965
|
def conclusion_type_hint(self) -> str:
|
@@ -906,8 +969,9 @@ class MultiClassRDR(RDRWithCodeWriter):
|
|
906
969
|
else:
|
907
970
|
return f"Set[Union[{', '.join(conclusion_types)}]]"
|
908
971
|
|
909
|
-
def _get_types_to_import(self) -> Tuple[Set[Type], Set[Type], Set[Type]]:
|
972
|
+
def _get_types_to_import(self) -> Tuple[Set[Union[Type, Callable]], Set[Type], Set[Type]]:
|
910
973
|
main_types, defs_types, cases_types = super()._get_types_to_import()
|
974
|
+
main_types.add(get_an_updated_case_copy)
|
911
975
|
main_types.update({Set, make_set})
|
912
976
|
defs_types.update({List, Set})
|
913
977
|
return main_types, defs_types, cases_types
|
@@ -939,28 +1003,43 @@ class MultiClassRDR(RDRWithCodeWriter):
|
|
939
1003
|
Stop a wrong conclusion by adding a stopping rule.
|
940
1004
|
"""
|
941
1005
|
rule_conclusion = evaluated_rule.conclusion(case_query.case)
|
942
|
-
|
943
|
-
|
944
|
-
|
1006
|
+
stop: bool = False
|
1007
|
+
add_filter_rule: bool = False
|
1008
|
+
if is_value_conflicting(rule_conclusion, case_query.target_value):
|
1009
|
+
if make_set(case_query.target_value).issubset(rule_conclusion):
|
1010
|
+
add_filter_rule = True
|
1011
|
+
else:
|
1012
|
+
stop = True
|
1013
|
+
elif make_set(case_query.core_attribute_type).issubset(make_set(evaluated_rule.conclusion.conclusion_type)):
|
1014
|
+
if make_set(case_query.target_value).issubset(rule_conclusion):
|
1015
|
+
add_filter_rule = True
|
1016
|
+
|
1017
|
+
if not stop:
|
945
1018
|
self.add_conclusion(rule_conclusion)
|
1019
|
+
if stop or add_filter_rule:
|
1020
|
+
refinement_type = MultiClassStopRule if stop else MultiClassFilterRule
|
1021
|
+
self.stop_or_filter_conclusion(case_query, expert, evaluated_rule, refinement_type=refinement_type)
|
946
1022
|
|
947
|
-
def
|
948
|
-
|
1023
|
+
def stop_or_filter_conclusion(self, case_query: CaseQuery,
|
1024
|
+
expert: Expert, evaluated_rule: MultiClassTopRule,
|
1025
|
+
refinement_type: Type[MultiClassRefinementRule] = MultiClassStopRule):
|
949
1026
|
"""
|
950
1027
|
Stop a conclusion by adding a stopping rule.
|
951
1028
|
|
952
1029
|
:param case_query: The case query to stop the conclusion for.
|
953
1030
|
:param expert: The expert to ask for differentiating features as new rule conditions.
|
954
1031
|
:param evaluated_rule: The evaluated rule to ask the expert about.
|
1032
|
+
:param refinement_type: The refinement type to use.
|
955
1033
|
"""
|
956
1034
|
conditions = expert.ask_for_conditions(case_query, evaluated_rule)
|
957
|
-
evaluated_rule.fit_rule(case_query)
|
958
|
-
if
|
959
|
-
self.
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
1035
|
+
evaluated_rule.fit_rule(case_query, refinement_type=refinement_type)
|
1036
|
+
if refinement_type is MultiClassStopRule:
|
1037
|
+
if self.mode == MCRDRMode.StopPlusRule:
|
1038
|
+
self.stop_rule_conditions = conditions
|
1039
|
+
if self.mode == MCRDRMode.StopPlusRuleCombined:
|
1040
|
+
new_top_rule_conditions = conditions.combine_with(evaluated_rule.conditions)
|
1041
|
+
case_query.conditions = new_top_rule_conditions
|
1042
|
+
self.add_top_rule(case_query)
|
964
1043
|
|
965
1044
|
def add_rule_for_case(self, case_query: CaseQuery, expert: Expert):
|
966
1045
|
"""
|
@@ -1064,8 +1143,8 @@ class GeneralRDR(RippleDownRules):
|
|
1064
1143
|
def start_rules(self) -> List[Union[SingleClassRule, MultiClassTopRule]]:
|
1065
1144
|
return [rdr.start_rule for rdr in self.start_rules_dict.values()]
|
1066
1145
|
|
1067
|
-
def
|
1068
|
-
|
1146
|
+
def _classify(self, case: Any, modify_case: bool = False,
|
1147
|
+
case_query: Optional[CaseQuery] = None) -> Optional[Dict[str, Any]]:
|
1069
1148
|
"""
|
1070
1149
|
Classify a case by going through all RDRs and adding the categories that are classified, and then restarting
|
1071
1150
|
the classification until no more categories can be added.
|
{ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/rdr_decorators.py
RENAMED
@@ -8,15 +8,16 @@ from functools import wraps
|
|
8
8
|
|
9
9
|
from typing_extensions import Callable, Optional, Type, Tuple, Dict, Any, Self, get_type_hints, List, Union, Sequence
|
10
10
|
|
11
|
-
from
|
12
|
-
from
|
13
|
-
from
|
14
|
-
from
|
11
|
+
from .datastructures.case import Case
|
12
|
+
from .datastructures.dataclasses import CaseQuery
|
13
|
+
from .experts import Expert, Human
|
14
|
+
from .rdr import GeneralRDR
|
15
|
+
from . import logger
|
15
16
|
try:
|
16
|
-
from
|
17
|
+
from .user_interface.gui import RDRCaseViewer
|
17
18
|
except ImportError:
|
18
19
|
RDRCaseViewer = None
|
19
|
-
from
|
20
|
+
from .utils import get_method_args_as_dict, get_func_rdr_model_name, make_set, \
|
20
21
|
get_method_class_if_exists, str_to_snake_case
|
21
22
|
|
22
23
|
|