kele 0.0.1a1__cp314-cp314-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. kele/__init__.py +38 -0
  2. kele/_version.py +1 -0
  3. kele/config.py +243 -0
  4. kele/control/README_metrics.md +102 -0
  5. kele/control/__init__.py +20 -0
  6. kele/control/callback.py +255 -0
  7. kele/control/grounding_selector/__init__.py +5 -0
  8. kele/control/grounding_selector/_rule_strategies/README.md +13 -0
  9. kele/control/grounding_selector/_rule_strategies/__init__.py +24 -0
  10. kele/control/grounding_selector/_rule_strategies/_sequential_strategy.py +42 -0
  11. kele/control/grounding_selector/_rule_strategies/strategy_protocol.py +51 -0
  12. kele/control/grounding_selector/_selector_utils.py +123 -0
  13. kele/control/grounding_selector/_term_strategies/__init__.py +24 -0
  14. kele/control/grounding_selector/_term_strategies/_exhausted_strategy.py +34 -0
  15. kele/control/grounding_selector/_term_strategies/strategy_protocol.py +50 -0
  16. kele/control/grounding_selector/rule_selector.py +98 -0
  17. kele/control/grounding_selector/term_selector.py +89 -0
  18. kele/control/infer_path.py +306 -0
  19. kele/control/metrics.py +357 -0
  20. kele/control/status.py +286 -0
  21. kele/egg_equiv.pyd +0 -0
  22. kele/egg_equiv.pyi +11 -0
  23. kele/equality/README.md +8 -0
  24. kele/equality/__init__.py +4 -0
  25. kele/equality/_egg_equiv/src/lib.rs +267 -0
  26. kele/equality/_equiv_elem.py +67 -0
  27. kele/equality/_utils.py +36 -0
  28. kele/equality/equivalence.py +141 -0
  29. kele/executer/__init__.py +4 -0
  30. kele/executer/executing.py +139 -0
  31. kele/grounder/README.md +83 -0
  32. kele/grounder/__init__.py +17 -0
  33. kele/grounder/grounded_rule_ds/__init__.py +6 -0
  34. kele/grounder/grounded_rule_ds/_nodes/__init__.py +24 -0
  35. kele/grounder/grounded_rule_ds/_nodes/_assertion.py +353 -0
  36. kele/grounder/grounded_rule_ds/_nodes/_conn.py +116 -0
  37. kele/grounder/grounded_rule_ds/_nodes/_op.py +57 -0
  38. kele/grounder/grounded_rule_ds/_nodes/_root.py +71 -0
  39. kele/grounder/grounded_rule_ds/_nodes/_rule.py +119 -0
  40. kele/grounder/grounded_rule_ds/_nodes/_term.py +390 -0
  41. kele/grounder/grounded_rule_ds/_nodes/_tftable.py +15 -0
  42. kele/grounder/grounded_rule_ds/_nodes/_tupletable.py +444 -0
  43. kele/grounder/grounded_rule_ds/_nodes/_typing_polars.py +26 -0
  44. kele/grounder/grounded_rule_ds/grounded_class.py +461 -0
  45. kele/grounder/grounded_rule_ds/grounded_ds_utils.py +91 -0
  46. kele/grounder/grounded_rule_ds/rule_check.py +373 -0
  47. kele/grounder/grounding.py +118 -0
  48. kele/knowledge_bases/README.md +112 -0
  49. kele/knowledge_bases/__init__.py +6 -0
  50. kele/knowledge_bases/builtin_base/__init__.py +1 -0
  51. kele/knowledge_bases/builtin_base/builtin_concepts.py +13 -0
  52. kele/knowledge_bases/builtin_base/builtin_facts.py +43 -0
  53. kele/knowledge_bases/builtin_base/builtin_operators.py +105 -0
  54. kele/knowledge_bases/builtin_base/builtin_rules.py +14 -0
  55. kele/knowledge_bases/fact_base.py +158 -0
  56. kele/knowledge_bases/ontology_base.py +67 -0
  57. kele/knowledge_bases/rule_base.py +194 -0
  58. kele/main.py +464 -0
  59. kele/py.typed +0 -0
  60. kele/syntax/CONCEPT_README.md +117 -0
  61. kele/syntax/__init__.py +40 -0
  62. kele/syntax/_cnf_converter.py +161 -0
  63. kele/syntax/_sat_solver.py +116 -0
  64. kele/syntax/base_classes.py +1482 -0
  65. kele/syntax/connectives.py +20 -0
  66. kele/syntax/dnf_converter.py +145 -0
  67. kele/syntax/external.py +17 -0
  68. kele/syntax/sub_concept.py +87 -0
  69. kele/syntax/syntacticsugar.py +201 -0
  70. kele-0.0.1a1.dist-info/METADATA +166 -0
  71. kele-0.0.1a1.dist-info/RECORD +74 -0
  72. kele-0.0.1a1.dist-info/WHEEL +4 -0
  73. kele-0.0.1a1.dist-info/licenses/LICENSE +28 -0
  74. kele-0.0.1a1.dist-info/licenses/licensecheck.json +20 -0
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+
7
+ from kele.syntax import Rule, Variable, Constant, CompoundTerm
8
+
9
+ if TYPE_CHECKING:
10
+ from kele.config import Config
11
+ from kele.grounder import GroundedRule
12
+ from ._tftable import TfTables
13
+ from collections.abc import Generator
14
+ from kele.syntax import FACT_TYPE
15
+ from kele.syntax.base_classes import _QuestionRule
16
+ from collections.abc import Mapping
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class _RuleNode:
22
+ """进入一个RuleNode节点表明当前规则已被满足,需要进行新事实的生成"""
23
+
24
+ def __init__(self, content: Rule, grounded_rule: GroundedRule, args: Config) -> None:
25
+ self.content: Rule = content
26
+ self.grounded_rule: GroundedRule = grounded_rule
27
+ self._args = args
28
+
29
+ self.left_table: TfTables # RuleNode的父节点只会有一个,我们默认为left_table来统一名称
30
+
31
+ def __str__(self) -> str:
32
+ return str(self.content)
33
+
34
+ def exec_check(self) -> list[FACT_TYPE]:
35
+ """
36
+ 返回所有的新事实,用于判断终止条件和更新事实库
37
+ :returns list[FACT_TYPE]: 新事实
38
+ """
39
+ return self._gen_new_facts()
40
+
41
+ def _gen_new_facts(self) -> list[FACT_TYPE]:
42
+ """FIXME: 这里要避免重复,可能是在grounding阶段就尽量移除掉重复的取值?不过此时的合并方式影响中途的去重"""
43
+ new_facts: list[FACT_TYPE] = []
44
+ if not self.left_table.true:
45
+ return new_facts
46
+
47
+ total_true_table = self.left_table.true
48
+
49
+ for combination in total_true_table.iter_rows():
50
+ new_fact = self.content.head.replace_variable(combination)
51
+ new_facts.append(new_fact)
52
+
53
+ if self._args.run.trace:
54
+ self._trace(new_fact, combination)
55
+
56
+ if self._args.executor.anti_join_used_facts:
57
+ self.grounded_rule.receive_true_table(total_true_table)
58
+
59
+ return new_facts
60
+
61
+ @staticmethod
62
+ def get_all_children() -> Generator[None]:
63
+ """
64
+ Yields:
65
+ None: 因为RuleNode没有子节点,所以返回None
66
+ """
67
+ yield None
68
+
69
+ def reset(self) -> None:
70
+ if hasattr(self, 'left_table'):
71
+ del self.left_table
72
+
73
+ def _trace(self, new_fact: FACT_TYPE, combination: Mapping[Variable, Constant | CompoundTerm]) -> None:
74
+ new_rule = Rule.from_parts(
75
+ head=new_fact,
76
+ body=self.content.body.replace_variable(combination),
77
+ priority=self.content.priority,
78
+ )
79
+
80
+ for head in new_rule.head_units:
81
+ self.grounded_rule.inference_path.add_infer_edge(consequent=head,
82
+ antecedents=new_rule.body_units,
83
+ grounded_rule=self.content.replace_variable(combination)) # FIXME: 这里
84
+ # 存一整个grounded rule的instance,是不是有点太重了。而如果只是存一个实例化的Rule instance,那好像又不需要传
85
+
86
+
87
+ class _QuestionRuleNode(_RuleNode):
88
+ """问题规则节点:额外负责收集查询解"""
89
+
90
+ def __init__(self, content: _QuestionRule, grounded_rule: GroundedRule, args: Config) -> None:
91
+ super().__init__(content, grounded_rule, args)
92
+ self.question_rule = content
93
+ self.solutions: list[Mapping[Variable, Constant | CompoundTerm]] = []
94
+
95
+ def _gen_new_facts(self) -> list[FACT_TYPE]: # TODO: 考虑与rule_node中的_gen_new_facts合并
96
+ """生成新事实的同时收集查询解"""
97
+ new_facts: list[FACT_TYPE] = []
98
+ if not self.left_table.true:
99
+ return new_facts
100
+
101
+ total_true_table = self.left_table.true
102
+ new_fact = self.content.head
103
+ new_facts.append(new_fact)
104
+
105
+ for combination in total_true_table.iter_rows():
106
+ self.solutions.append(combination)
107
+
108
+ if self._args.run.trace:
109
+ self._trace(new_fact, combination)
110
+
111
+ if self._args.executor.anti_join_used_facts:
112
+ self.grounded_rule.receive_true_table(total_true_table)
113
+
114
+ return new_facts
115
+
116
+ def reset(self) -> None:
117
+ """重写 reset:额外清空 solutions"""
118
+ super().reset()
119
+ self.solutions.clear()
@@ -0,0 +1,390 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ import logging
5
+ from typing import TYPE_CHECKING, cast
6
+
7
+ from kele.syntax import (TERM_TYPE, FlatCompoundTerm, Constant, CompoundTerm, Operator,
8
+ Variable, FLATTERM_TYPE, ATOM_TYPE)
9
+
10
+ from collections import OrderedDict
11
+ from ._tupletable import _TupleTable
12
+ from ._op import _OperatorNode
13
+
14
+ from kele.grounder.grounded_rule_ds.grounded_ds_utils import flatten_arguments, FREEANY
15
+ # HACK: 一会儿想想如何调整逻辑,按说有一个地方用就够了
16
+ # 替代方案是,在构造节点的时候(composite_node = _FlatCompoundTermNode(self.grounded_rule, term_or_constant=cur_term)),
17
+ # 生成一个额外的、执行过_flatten_arguments的term作为入参,虽然比在_FlatCompoundTermNode里面直接对atom_arguments赋值时候
18
+ # 进行_flatten_arguments操作要花费更多的时间空间,但利于代码组织。
19
+ # 不过另一方面,FlatCompoundTermNode.term_or_constant的类型是TERM_TYPE,如果用这个替代方案就意味着类型应该是Constant, Varibale和变更为FREEANY
20
+ # 的CompoundTerm,就会有点不匹配。解决方案是当完全以效率优先是,用于绘图、展示等需求的term_or_constant应当被直接替换为arguments,
21
+ # 从而类型就变成了Constant | Va....,就不存在这个问题了。因此既然替代方案最终会被丢弃,暂时就先注释掉这个报错且从FIXME修改为hack。
22
+
23
+ if TYPE_CHECKING:
24
+ from kele.config import Config
25
+ from ._assertion import _AssertionNode
26
+ from ._root import _RootNode
27
+ from collections.abc import Generator
28
+ from kele.grounder import GroundedRule
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class _FlatCompoundTermNode(ABC):
34
+ """
35
+ 这是原子 CompoundTermNode,其参数中不再含嵌套 CompoundTerm。
36
+
37
+ FlatCompoundTermNode 可以连接到 AssertionNode,也可以继续连接到更外层的 term 节点。
38
+ """
39
+ composite_or_assertion_child: list[_FlatCompoundTermNode | _AssertionNode]
40
+ raw_atom_arguments: tuple[ATOM_TYPE, ...] # FIXME: 原始的atom_arguments,似乎没有其他需要调用它的地方
41
+ represent_arguments: tuple[TERM_TYPE, ...]
42
+ grounding_arguments: tuple[Variable, ...] # 当前FlatTerm的所有(去重后的)Variable,例如op(x, $F)的grounding_arguments为(x,)
43
+ _term_or_constant: TERM_TYPE
44
+ only_substitution: bool
45
+
46
+ grounded_rule: GroundedRule
47
+
48
+ freevar_table: _TupleTable
49
+ all_freevar_table: list[_TupleTable]
50
+
51
+ operator: Operator
52
+ able_to_pass_freevar: bool # 只有当此层级的FlatTerm存在Variable的时候,也就是grounding_arguments不为空/表列数不为0的时候,
53
+ # 才会向GroundedRule传递table(但按图结构的向下pass是不影响的,向GroundedRule传递实则代表是否被用于total table的生成)
54
+ # 例如op(x, $F)会向GroundedRule传递,但是op($F, $F)不会传递
55
+
56
+ def add_child(self, node: _FlatCompoundTermNode | _AssertionNode) -> None:
57
+ self.composite_or_assertion_child.append(node)
58
+
59
+ def __str__(self) -> str:
60
+ return str(self._term_or_constant)
61
+
62
+ @abstractmethod
63
+ def exec_unify(self,
64
+ term: CompoundTerm[Constant | CompoundTerm] | Constant,
65
+ *,
66
+ allow_unify_with_nested_term: bool = True
67
+ ) -> None:
68
+ """
69
+ 执行unify操作
70
+ 在useful_terms的控制下,传入的一定是FlatCompoundTerm
71
+
72
+ :param term (CompoundTerm | Constant): 待实例化的Term,某种意义上只有可能是FlatCompoundTerm,但是为了避免外层的类型检查,
73
+ 我们还是采纳这种写法
74
+ :param allow_unify_with_nested_term: 是否允许与嵌套的Term进行unify
75
+
76
+ FIXME: 此外应考虑机器剩余核数并考虑如何并行处理。不过这个可能是在executor的check中处理
77
+ """
78
+
79
+ @abstractmethod
80
+ def process_equiv_represent_elem(self) -> None:
81
+ """
82
+ 将常量替换为等价类代表元,用于统一匹配与表合并。
83
+ """
84
+
85
+ def pass_freevar_to_child(self) -> None:
86
+ """
87
+ 将 freevar_table 传递给子节点,并在需要时加入 GroundedRule 的合并列表。
88
+ """
89
+ if logger.isEnabledFor(logging.DEBUG):
90
+ logger.debug(
91
+ "Term node freevar table prepared: node=%s only_substitution=%s able_to_pass=%s summary=%s",
92
+ self.node_representative,
93
+ self.only_substitution,
94
+ self.able_to_pass_freevar,
95
+ self.freevar_table.debug_summary(),
96
+ )
97
+
98
+ if self.able_to_pass_freevar and not self.only_substitution:
99
+ self.all_freevar_table.append(self.freevar_table)
100
+ for child in self.composite_or_assertion_child:
101
+ child.all_freevar_table.extend(self.all_freevar_table) # FIXME: 这里值得及时清空吗?
102
+
103
+ def query_for_child(self, term: FLATTERM_TYPE | None = None) -> tuple[_AssertionNode | _FlatCompoundTermNode, ...]:
104
+ """
105
+ 获得所有子节点,参数term为可选参数,照理来说没啥用,仅用于统一格式
106
+ """
107
+ return tuple(self.composite_or_assertion_child)
108
+
109
+ def get_all_children(self) -> Generator[_FlatCompoundTermNode | _AssertionNode]:
110
+ """
111
+ 仅用于绘图使用
112
+ """ # noqa: DOC402
113
+ yield from self.composite_or_assertion_child
114
+
115
+ def get_free_var_name(self) -> str:
116
+ """
117
+ 仅用于绘图使用
118
+ """
119
+ variable_name = self.freevar_table.raw_column_name
120
+ print_dict = _TupleTable(variable_name)
121
+ print_dict.set_base_df(self.freevar_table.base_df)
122
+
123
+ variables_str: list[str] = print_dict.raw_columns_name_str
124
+ result_str = ','.join(variables_str) + '\n'
125
+
126
+ values_str = []
127
+ for var_value in print_dict.iter_rows():
128
+ var_value_str = [str(v) for v in var_value.values()]
129
+ single_str = ','.join(var_value_str)
130
+ values_str.append(single_str)
131
+
132
+ return result_str + '\n'.join(values_str)
133
+
134
+ def _add_uncheck_results(self) -> None:
135
+ """将实例化的匹配信息传递回grounded rule"""
136
+ raise NotImplementedError
137
+
138
+ @property
139
+ def node_representative(self) -> str: # 注意其他节点如果需要打印信息时,也可以采用相似的同名函数
140
+ """用于绘图中,代表当前node的简短信息"""
141
+ return str(self._term_or_constant)
142
+
143
+ def reset(self) -> None:
144
+ self.all_freevar_table.clear()
145
+ self.freevar_table.clear() # 不应该在join操作之前将all_freevar_table给清空,否则由于传递的是指针会导致错误
146
+ # 其他tf index和joint类同
147
+
148
+
149
+ class _VariableNode(_FlatCompoundTermNode):
150
+ def __init__(self,
151
+ rule: GroundedRule,
152
+ term_or_constant: Variable,
153
+ *,
154
+ only_substitution: bool = False) -> None:
155
+ self.grounded_rule = rule
156
+ self.composite_or_assertion_child: list[_FlatCompoundTermNode | _AssertionNode] = []
157
+
158
+ self.raw_atom_arguments = (term_or_constant,)
159
+ self.grounding_arguments = self.raw_atom_arguments
160
+
161
+ self.only_substitution = only_substitution
162
+
163
+ self.freevar_table: _TupleTable = _TupleTable(self.grounding_arguments)
164
+ self.all_freevar_table: list[_TupleTable] = []
165
+
166
+ self._term_or_constant: Variable = term_or_constant
167
+ self.able_to_pass_freevar: bool = True
168
+
169
+ def process_equiv_represent_elem(self) -> None:
170
+ pass
171
+
172
+ def exec_unify(self,
173
+ term: CompoundTerm[Constant | CompoundTerm] | Constant,
174
+ *,
175
+ allow_unify_with_nested_term: bool = True) -> None:
176
+ # 这种情况是直接连接RootNode的Variable,此时传入的任何东西都会作为可能的候选值
177
+ if ((isinstance(term, Constant) or
178
+ (isinstance(term, CompoundTerm) and allow_unify_with_nested_term)) and
179
+ self.grounded_rule.is_concept_compatible_binding(self._term_or_constant, term)
180
+ ):
181
+ self.freevar_table.add_row({self._term_or_constant: term})
182
+ # 当节点类型为Constant时,由_RootNode的query_for_children函数来保证可以匹配,而不必执行unify操作 fixme: Constant可能更名const
183
+
184
+
185
+ class _TermNode(_FlatCompoundTermNode):
186
+ def __init__(self,
187
+ rule: GroundedRule,
188
+ term_or_constant: CompoundTerm,
189
+ *,
190
+ only_substitution: bool = False) -> None:
191
+ self.grounded_rule = rule
192
+ self.composite_or_assertion_child: list[_FlatCompoundTermNode | _AssertionNode] = []
193
+
194
+ self.atom_arguments: tuple[TERM_TYPE, ...] = flatten_arguments(term_or_constant.arguments)
195
+ self.represent_arguments = self.atom_arguments # 初始化代表元组和原本的一致
196
+ self.grounding_arguments = tuple(u for u in self.atom_arguments if isinstance(u, Variable))
197
+ self.grounding_arguments = tuple(OrderedDict.fromkeys(self.grounding_arguments))
198
+ self.operator = term_or_constant.operator
199
+
200
+ self.only_substitution = only_substitution
201
+
202
+ self.freevar_table: _TupleTable = _TupleTable(self.grounding_arguments)
203
+ self.all_freevar_table: list[_TupleTable] = []
204
+
205
+ self._term_or_constant: CompoundTerm = term_or_constant
206
+ self.able_to_pass_freevar: bool = self.grounding_arguments != () # grounding_arguments非空才能向下传递
207
+
208
+ def process_equiv_represent_elem(self) -> None:
209
+ """
210
+ 将所有的Constant替换为等价类代表元
211
+ """
212
+ processed_represent_arguments: list[TERM_TYPE] = []
213
+ for arg in self.represent_arguments:
214
+ if arg is not FREEANY and isinstance(arg, Constant):
215
+ processed_represent_arguments.append(self.grounded_rule.equivalence.get_represent_elem(arg))
216
+ else:
217
+ processed_represent_arguments.append(arg)
218
+ self.represent_arguments = tuple(processed_represent_arguments)
219
+
220
+ def _unify_the_term(self,
221
+ term_fact: CompoundTerm[Constant | CompoundTerm],
222
+ *,
223
+ allow_unify_with_nested_term: bool = True) -> None:
224
+ """
225
+ 将TermNode的unify过程单独放在这里,减少for循环的次数
226
+ 以下情况,我们认为term_fact可以用来实例化flat_term:
227
+ 1、对应位置上,flat_term是Variable,此时term_fact随便是什么都可以
228
+ 2、对应位置上,flat_term是FREEANY,此时term_fact随便是什么都可以(存疑,也许应当要求term_fact这时候也是FREEANY)
229
+ 3、对应位置都是Constant,而且他们一致
230
+ 注意:这里使用的是 self.represent_arguments,它已完成等价类代表元替换。
231
+ """
232
+ # 例如op(x,y,1),现在记录的应该就是(x,y,1):[(1,2,1), (1,3,1),(2,3,1),....]这样的
233
+ temp_arguments: dict[Variable, Constant | CompoundTerm] = {}
234
+
235
+ for _i, (s_rule, s_fact) in enumerate(zip(self.represent_arguments, term_fact.arguments, strict=True)):
236
+ if TYPE_CHECKING:
237
+ s_fact = cast("Constant | CompoundTerm", s_fact)
238
+
239
+ s_fact_represent = self.grounded_rule.equivalence.get_represent_elem(s_fact)
240
+
241
+ # risk:如果规则中为op1(x,y,op2(z)),出现了事实op1(1,2,3),照理来说也许不应当实例化,但是此处我们仍然会将(1,2)作为(x,y)的候选值
242
+
243
+ if isinstance(s_rule, Variable):
244
+ if ((s_rule in temp_arguments and temp_arguments[s_rule] != s_fact_represent) or
245
+ # 同名变量多次出现时必须绑定到同一个值(例如 add(x, x) 不能与 add(1, 2) 匹配)
246
+ not self.grounded_rule.is_concept_compatible_binding(s_rule, s_fact_represent)):
247
+ # Rule-level variable concept constraints check (e.g., constraints propagated via x=y)
248
+ return
249
+
250
+ if (isinstance(s_fact_represent, Constant) and
251
+ self.grounded_rule.is_concept_compatible_binding(s_rule, s_fact_represent)):
252
+ # s_fact_represent为constant的情况
253
+ temp_arguments[s_rule] = s_fact_represent
254
+
255
+ elif ((allow_unify_with_nested_term and isinstance(s_fact_represent, CompoundTerm))
256
+ and self.grounded_rule.is_concept_compatible_binding(s_rule, s_fact_represent)):
257
+ # s_fact_represent为CompoundTerm的情况
258
+ temp_arguments[s_rule] = s_fact_represent
259
+ else:
260
+ return
261
+
262
+ elif isinstance(s_rule, Constant) and s_rule != s_fact and s_rule is not FREEANY:
263
+ if s_rule is FREEANY:
264
+ continue
265
+ if s_rule == s_fact_represent:
266
+ # s_fact 与 s_rule 已替换为等价类代表元,因此直接比较即可
267
+ continue
268
+ # 当Rule在此处取值为Constant时,如果rule和fact对应位置不一致,应该视为匹配失败
269
+ # FREEANY虽然是特殊的Constant,但是此处不受到限制(因为它是占位符)
270
+ return
271
+
272
+ self.freevar_table.add_row(temp_arguments)
273
+
274
+ def exec_unify(self,
275
+ term: CompoundTerm[Constant | CompoundTerm] | Constant,
276
+ *,
277
+ allow_unify_with_nested_term: bool = True) -> None:
278
+ if isinstance(term, CompoundTerm):
279
+ # 针对term的情况,只有传入的是CompoundTerm才会开始Unify操作
280
+ self._unify_the_term(term, allow_unify_with_nested_term=allow_unify_with_nested_term)
281
+
282
+
283
+ class _ConstantNode(_FlatCompoundTermNode):
284
+ def __init__(self, rule: GroundedRule, term_or_constant: Constant) -> None:
285
+ self.grounded_rule = rule
286
+ self.composite_or_assertion_child: list[_FlatCompoundTermNode | _AssertionNode] = []
287
+
288
+ self.raw_atom_arguments = (term_or_constant,)
289
+ self.represent_arguments = (term_or_constant,)
290
+ self.grounding_arguments = () # fixme: 常量这里会不会为空就行?
291
+
292
+ self.freevar_table: _TupleTable = _TupleTable(())
293
+ self.all_freevar_table: list[_TupleTable] = []
294
+
295
+ self._term_or_constant: Constant = term_or_constant
296
+ self.able_to_pass_freevar: bool = False
297
+
298
+ self.only_substitution = False
299
+
300
+ def process_equiv_represent_elem(self) -> None:
301
+ """
302
+ 将常量替换为等价类代表元。
303
+ """
304
+ self.represent_arguments = (self.grounded_rule.equivalence.get_represent_elem(self._term_or_constant), )
305
+
306
+ def exec_unify(self,
307
+ term: CompoundTerm[Constant | CompoundTerm] | Constant,
308
+ *,
309
+ allow_unify_with_nested_term: bool = True,
310
+ fuzzy_match: bool = True) -> None:
311
+ pass
312
+
313
+
314
+ def build_termnode(rule: GroundedRule, term_or_constant: TERM_TYPE) -> _FlatCompoundTermNode:
315
+ """
316
+ 工厂函数,用于创建合适的FlatCompoundTerm
317
+ :param rule: 用于创建Node的groundedRule
318
+ :param term_or_constant: 用于创建Node的term
319
+ :return: 合适的FlatCompoundTerm
320
+
321
+ :raises ValueError: 如果term_or_constant不是CompoundTerm、Constant或Variable
322
+
323
+ """ # noqa: DOC501
324
+ if isinstance(term_or_constant, CompoundTerm):
325
+ return _TermNode(rule, term_or_constant)
326
+ if isinstance(term_or_constant, Constant):
327
+ return _ConstantNode(rule, term_or_constant)
328
+ if isinstance(term_or_constant, Variable):
329
+ return _VariableNode(rule, term_or_constant)
330
+ raise ValueError("term_or_constant must be CompoundTerm, Constant or Variable")
331
+
332
+
333
+ class _BuildTerm:
334
+ """
335
+ 进行实例化的单位,将题目拆解为一个个Term分别匹配和存储可能的实例化。对Term中变量的实例化候选值,将存储到GroundedRule的free_variables中。
336
+ 目前这个类作为一个过渡类,在创建的时候会将Term拆分为FlatCompoundTermNode和TermConstantNode,创建内部的图结构。
337
+ 以后将以FlatCompoundTermNode | TermConstantNode作为基本单位进行操作
338
+ """
339
+
340
+ def __init__(self, grounded_rule: GroundedRule, root_node: _RootNode, args: Config) -> None:
341
+ self.grounded_rule = grounded_rule # 用于传回free_variables里面
342
+ self.root_node = root_node
343
+ self.args = args # TODO: 需要这么去传args,说明可能有优化余地
344
+
345
+ def build_term_structure(self, cur_term: TERM_TYPE, root_node: _RootNode, *, only_substitution: bool = False) -> _FlatCompoundTermNode:
346
+ """
347
+ 对于当前的term,创建对应的FlatCompoundTermNode,如果有复合结构,那么拆开复合结构而构建图结构
348
+
349
+ :param cur_term: 当前的term
350
+ :param root_node: 根节点
351
+ :return: 对应的FlatCompoundTermNode
352
+ """
353
+ if isinstance(cur_term, (Constant, Variable)): # 只有Constant|Variable|FlatTerm的情况会在这里处理
354
+ # 直接返回即可,拆解到此为止
355
+ atom_node = build_termnode(self.grounded_rule, cur_term)
356
+ # 本来被标记为only_substitution的不会再改变其状态
357
+ # 严格来说此构图代码只会被触发一次,此处写法是保险起见
358
+ atom_node.only_substitution = True if atom_node.only_substitution else only_substitution
359
+ root_node.add_child(atom_node)
360
+ return atom_node
361
+
362
+ # 此后,cur_term一定是CompoundTerm,进而term_node一定是_TermNode
363
+ term_node = build_termnode(self.grounded_rule, cur_term)
364
+ term_node.only_substitution = True if term_node.only_substitution else only_substitution
365
+
366
+ if term_node.able_to_pass_freevar:
367
+ # 只有当需要传递free_var(也即grounding_arguments非空)才需要创建operatorNode
368
+ op_node = self._build_operator_node(cur_term, root_node)
369
+ op_node.add_child(term_node)
370
+ elif isinstance(cur_term, (FlatCompoundTerm, Constant)):
371
+ # 这是完全没有Variable的AtomCompundTerm,无需连接到OperatorNode,但是需要连接到RootNode
372
+ root_node.add_child(term_node)
373
+
374
+ for single_term in cur_term.arguments:
375
+ if isinstance(single_term, CompoundTerm):
376
+ father_node = self.build_term_structure(cur_term=single_term, root_node=root_node)
377
+ father_node.add_child(term_node)
378
+
379
+ return term_node
380
+
381
+ def _build_operator_node(self, single_term: CompoundTerm, root_node: _RootNode) -> _OperatorNode:
382
+ re_operator_node = root_node.operator_exist(single_term.operator)
383
+ if re_operator_node:
384
+ # 之前创建过这个Operator的_OperatorNode,直接返回这个OperatorNode就好
385
+ return re_operator_node
386
+ # 这是全新的_FlatCompoundTermNode,现在的FlatCompoundTermNode是由buildterm的入口传来的,而它作为入口意味着一定
387
+ # 有创建_OperatorNode的必要.如果_FlatCompoundTermNode全是由复合的Term组成的,那他就没有必要连接OperatorNode,这里也不会传入
388
+ operator_node = _OperatorNode(single_term.operator)
389
+ self.root_node.add_child(operator_node)
390
+ return operator_node
@@ -0,0 +1,15 @@
1
+ from typing import NamedTuple
2
+ from ._tupletable import _TupleTable
3
+
4
+
5
+ class TfTables(NamedTuple):
6
+ """
7
+ 类TfIndexs,记录了经过一个节点之后的true_index和false_index
8
+ """
9
+ true: _TupleTable
10
+ false: _TupleTable
11
+
12
+ def clear(self) -> None:
13
+ """本函数应当谨慎使用。由于效率缘故,exec阶段会以指针形式传递tfindex相关字段,应在确定某字段使用完毕后,再执行"""
14
+ self.true.clear()
15
+ self.false.clear()