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.
Files changed (153) hide show
  1. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/PKG-INFO +1 -1
  2. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/scripts/live_dot_server_client.py +1 -1
  3. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/__init__.py +1 -1
  4. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/callable_expression.py +3 -2
  5. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/dataclasses.py +2 -2
  6. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/enums.py +4 -1
  7. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/experts.py +2 -2
  8. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/helpers.py +28 -7
  9. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/rdr.py +113 -34
  10. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/rdr_decorators.py +7 -6
  11. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/rules.py +160 -56
  12. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/prompt.py +6 -3
  13. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/utils.py +30 -15
  14. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/PKG-INFO +1 -1
  15. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_json_serialization.py +8 -3
  16. {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
  17. {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
  18. {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
  19. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/test_qt_gui_inline.py +3 -2
  20. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.github/workflows/build_and_deploy_doc.yml +0 -0
  21. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.github/workflows/ci.yml +0 -0
  22. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.github/workflows/publish-to-test-pypi.yml +0 -0
  23. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/.gitignore +0 -0
  24. {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
  25. {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
  26. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/LICENSE +0 -0
  27. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/README.md +0 -0
  28. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/_config.yml +0 -0
  29. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/_toc.yml +0 -0
  30. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/bibliography.md +0 -0
  31. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/intro.md +0 -0
  32. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/references.bib +0 -0
  33. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/doc/requirements.txt +0 -0
  34. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/__init__.py +0 -0
  35. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/animal_species.py +0 -0
  36. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/__init__.py +0 -0
  37. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/rdr_metadata/part_containment_rdr.json +0 -0
  38. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/robot_contained_objects_mcrdr.py +0 -0
  39. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/robot_contained_objects_mcrdr_defs.py +0 -0
  40. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/part_containment_rdr/robot_rdr.py +0 -0
  41. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/examples/relational_example.py +0 -0
  42. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/images/scrdr.dot +0 -0
  43. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/images/scrdr.png +0 -0
  44. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/images/thinking_pr2.jpg +0 -0
  45. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/pyproject.toml +0 -0
  46. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/pytest.ini +0 -0
  47. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements-dev-ci.txt +0 -0
  48. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements-dev.txt +0 -0
  49. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements-gui.txt +0 -0
  50. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements-viz.txt +0 -0
  51. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/requirements.txt +0 -0
  52. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_extra.dot +0 -0
  53. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_extra.png +0 -0
  54. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_stop_only.dot +0 -0
  55. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_stop_only.png +0 -0
  56. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_stop_plus_rule.dot +0 -0
  57. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_mcrdr_stop_plus_rule.png +0 -0
  58. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr.dot +0 -0
  59. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr.png +0 -0
  60. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr_2.dot +0 -0
  61. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr_2.png +0 -0
  62. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr_3.dot +0 -0
  63. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/complete_scrdr_3.png +0 -0
  64. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/grdr_Habitat.dot +0 -0
  65. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/grdr_Habitat.png +0 -0
  66. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/grdr_Species.dot +0 -0
  67. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/grdr_Species.png +0 -0
  68. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_extra.dot +0 -0
  69. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_extra.png +0 -0
  70. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_extra_classify.dot +0 -0
  71. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_extra_classify.png +0 -0
  72. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_stop_plus_rule_combined.dot +0 -0
  73. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/mcrdr_stop_plus_rule_combined.png +0 -0
  74. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/partial_mcrdr_extra.dot +0 -0
  75. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/partial_mcrdr_extra.png +0 -0
  76. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/relational_scrdr_classify.dot +0 -0
  77. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/results/relational_scrdr_classify.png +0 -0
  78. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/setup.cfg +0 -0
  79. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/__init__.py +0 -0
  80. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/datastructures/case.py +0 -0
  81. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/start-code-server.sh +0 -0
  82. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/__init__.py +0 -0
  83. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/gui.py +0 -0
  84. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/ipython_custom_shell.py +0 -0
  85. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/object_diagram.py +0 -0
  86. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules/user_interface/template_file_creator.py +0 -0
  87. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/SOURCES.txt +0 -0
  88. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
  89. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/requires.txt +0 -0
  90. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
  91. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/__init__.py +0 -0
  92. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conf/__init__.py +0 -0
  93. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conf/world/__init__.py +0 -0
  94. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conf/world/base_config.py +0 -0
  95. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conf/world/handles_and_containers.py +0 -0
  96. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/conftest.py +0 -0
  97. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/datasets.py +0 -0
  98. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/factories/__init__.py +0 -0
  99. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/factories/world/__init__.py +0 -0
  100. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/factories/world/handles_and_containers.py +0 -0
  101. {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
  102. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/drawer_cabinet_expert_answers_fit.json +0 -0
  103. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/grdr_expert_answers_classify.json +0 -0
  104. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/grdr_expert_answers_fit.json +0 -0
  105. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/grdr_expert_answers_fit_extra.json +0 -0
  106. {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
  107. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_expert_answers_classify.json +0 -0
  108. {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
  109. {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
  110. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_extra_expert_answers_classify.json +0 -0
  111. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_extra_expert_answers_fit.json +0 -0
  112. {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
  113. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mcrdr_stop_only_answers_fit.json +0 -0
  114. {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
  115. {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
  116. {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
  117. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/mutagenic_expert_answers.json +0 -0
  118. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/relational_scrdr_expert_answers_classify.json +0 -0
  119. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/scrdr_expert_answers_classify.json +0 -0
  120. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/scrdr_expert_answers_fit.json +0 -0
  121. {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
  122. {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
  123. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_expert_answers/scrdr_world_expert_answers_fit.json +0 -0
  124. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_generated_rdrs/__init__.py +0 -0
  125. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_helpers/__init__.py +0 -0
  126. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_helpers/helpers.py +0 -0
  127. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_helpers/object_diagram_case_query.png +0 -0
  128. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_helpers/object_diagram_person.png +0 -0
  129. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_object_diagram.py +0 -0
  130. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_on_mutagenic.py +0 -0
  131. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr.py +0 -0
  132. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_alchemy.py +0 -0
  133. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_decorators.py +0 -0
  134. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_helpers_rdrs.py +0 -0
  135. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_world/__init__.py +0 -0
  136. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_world/conftest.py +0 -0
  137. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_rdr_world/test_rdr_world.py +0 -0
  138. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_relational_rdr.py +0 -0
  139. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_relational_rdr_alchemy.py +0 -0
  140. {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
  141. {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
  142. {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
  143. {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
  144. {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
  145. {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
  146. {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
  147. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_sql_model.py +0 -0
  148. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_template_file_creator.py +0 -0
  149. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/__init__.py +0 -0
  150. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/test_ipython.py +0 -0
  151. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/test_ipython_copilot.py +0 -0
  152. {ripple_down_rules-0.6.23 → ripple_down_rules-0.6.25}/test/test_user_interface/test_prompt.py +0 -0
  153. {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.23
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
@@ -52,7 +52,7 @@ def generate_html(dot_output="graph.html"):
52
52
  }}
53
53
 
54
54
  fetchAndRender();
55
- setInterval(fetchAndRender, 500); // Refresh every 2 seconds
55
+ setInterval(fetchAndRender, 100); // Refresh every 2 seconds
56
56
  </script>
57
57
  </body>
58
58
  </html>"""
@@ -1,4 +1,4 @@
1
- __version__ = "0.6.23"
1
+ __version__ = "0.6.25"
2
2
 
3
3
  import logging
4
4
  logger = logging.Logger("rdr")
@@ -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 all(t in self.attribute_type for t in [list, set]) and len(self.core_attribute_type) > 2:
142
+ if not self.mutually_exclusive:
143
143
  return f"List[{attribute_types_str}]"
144
144
  else:
145
145
  return attribute_types_str
@@ -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 = True,
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 .utils import get_func_rdr_model_name, copy_case, make_set, update_case
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())[0].mutually_exclusive:
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, pred_cat: Optional[Dict[str, Any]] = None) -> bool:
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
- is_conflicting, extract_function_source, extract_imports, get_full_class_name, \
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
- render_tree(self.start_rule, use_dot_exporter=True, filename=filename)
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
- # raise ValueError("The start rule is not set. Please set the start rule before getting the evaluated rule tree.")
129
- evaluated_rule_tree = [r for r in [self.start_rule] + list(self.start_rule.descendants) if r.evaluated]
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 classify(self, case: Union[Case, SQLTable], modify_case: bool = False,
265
- case_query: Optional[CaseQuery] = None) \
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
- rule.conditions.user_input = functions_source[f"conditions_{rule.uid}"]
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 rule.conclusion is not None and not isinstance(rule, MultiClassStopRule):
487
- rule.conclusion.user_input = functions_source[f"conclusion_{rule.uid}"]
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 classify(self, case: Case, modify_case: bool = False,
707
- case_query: Optional[CaseQuery] = None) -> Optional[Any]:
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 classify(self, case: Union[Case, SQLTable], modify_case: bool = False,
822
- case_query: Optional[CaseQuery] = None) -> Set[Any]:
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
- if is_conflicting(rule_conclusion, case_query.target_value):
943
- self.stop_conclusion(case_query, expert, evaluated_rule)
944
- else:
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 stop_conclusion(self, case_query: CaseQuery,
948
- expert: Expert, evaluated_rule: MultiClassTopRule):
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 self.mode == MCRDRMode.StopPlusRule:
959
- self.stop_rule_conditions = conditions
960
- if self.mode == MCRDRMode.StopPlusRuleCombined:
961
- new_top_rule_conditions = conditions.combine_with(evaluated_rule.conditions)
962
- case_query.conditions = new_top_rule_conditions
963
- self.add_top_rule(case_query)
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 classify(self, case: Any, modify_case: bool = False,
1068
- case_query: Optional[CaseQuery] = None) -> Optional[Dict[str, Any]]:
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.
@@ -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 ripple_down_rules.datastructures.case import Case
12
- from ripple_down_rules.datastructures.dataclasses import CaseQuery
13
- from ripple_down_rules.experts import Expert, Human
14
- from ripple_down_rules.rdr import GeneralRDR
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 ripple_down_rules.user_interface.gui import RDRCaseViewer
17
+ from .user_interface.gui import RDRCaseViewer
17
18
  except ImportError:
18
19
  RDRCaseViewer = None
19
- from ripple_down_rules.utils import get_method_args_as_dict, get_func_rdr_model_name, make_set, \
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