You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

kb.py 25 kB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. import bisect
  2. import inspect
  3. import logging
  4. import os
  5. from abc import ABC, abstractmethod
  6. from collections import defaultdict
  7. from itertools import combinations, product
  8. from multiprocessing import Pool
  9. from typing import Any, Callable, List, Optional
  10. import numpy as np
  11. from ..utils.cache import abl_cache
  12. from ..utils.logger import print_log
  13. from ..utils.utils import flatten, hamming_dist, reform_list, to_hashable
  14. class KBBase(ABC):
  15. """
  16. Base class for knowledge base.
  17. Parameters
  18. ----------
  19. pseudo_label_list : List[Any]
  20. List of possible pseudo-labels. It's recommended to arrange the pseudo-labels in this
  21. list so that each aligns with its corresponding index in the base model: the first with
  22. the 0th index, the second with the 1st, and so forth.
  23. max_err : float, optional
  24. The upper tolerance limit when comparing the similarity between the reasoning result of
  25. pseudo-labels and the ground truth. This is only applicable when the reasoning
  26. result is of a numerical type. This is particularly relevant for regression problems where
  27. exact matches might not be feasible. Defaults to 1e-10.
  28. use_cache : bool, optional
  29. Whether to use abl_cache for previously abduced candidates to speed up subsequent
  30. operations. Defaults to True.
  31. key_func : Callable, optional
  32. A function employed for hashing in abl_cache. This is only operational when use_cache
  33. is set to True. Defaults to ``to_hashable``.
  34. cache_size: int, optional
  35. The cache size in abl_cache. This is only operational when use_cache is set to
  36. True. Defaults to 4096.
  37. Notes
  38. -----
  39. Users should derive from this base class to build their own knowledge base. For the
  40. user-build KB (a derived subclass), it's only required for the user to provide the
  41. ``pseudo_label_list`` and override the ``logic_forward`` function (specifying how to
  42. perform logical reasoning). After that, other operations (e.g. how to perform abductive
  43. reasoning) will be automatically set up.
  44. """
  45. def __init__(
  46. self,
  47. pseudo_label_list: List[Any],
  48. max_err: float = 1e-10,
  49. use_cache: bool = True,
  50. key_func: Callable = to_hashable,
  51. cache_size: int = 4096,
  52. ):
  53. if not isinstance(pseudo_label_list, list):
  54. raise TypeError(f"pseudo_label_list should be list, got {type(pseudo_label_list)}")
  55. self.pseudo_label_list = pseudo_label_list
  56. self.max_err = max_err
  57. self.use_cache = use_cache
  58. self.key_func = key_func
  59. self.cache_size = cache_size
  60. argspec = inspect.getfullargspec(self.logic_forward)
  61. self._num_args = len(argspec.args) - 1
  62. if (
  63. self._num_args == 2 and self.use_cache
  64. ): # If the logic_forward function has 2 arguments, then disable cache
  65. self.use_cache = False
  66. print_log(
  67. "The logic_forward function has 2 arguments, so the cache is disabled. ",
  68. logger="current",
  69. level=logging.WARNING,
  70. )
  71. @abstractmethod
  72. def logic_forward(self, pseudo_label: List[Any], x: Optional[List[Any]] = None) -> Any:
  73. """
  74. How to perform (deductive) logical reasoning, i.e. matching an example's
  75. pseudo-labels to its reasoning result. Users are required to provide this.
  76. Parameters
  77. ----------
  78. pseudo_label : List[Any]
  79. Pseudo-labels of an example.
  80. x : List[Any], optional
  81. The example. If deductive logical reasoning does not require any
  82. information from the example, the overridden function provided by the user can omit
  83. this parameter.
  84. Returns
  85. -------
  86. Any
  87. The reasoning result.
  88. """
  89. def abduce_candidates(
  90. self,
  91. pseudo_label: List[Any],
  92. y: Any,
  93. x: List[Any],
  94. max_revision_num: int,
  95. require_more_revision: int,
  96. ) -> List[List[Any]]:
  97. """
  98. Perform abductive reasoning to get a candidate compatible with the knowledge base.
  99. Parameters
  100. ----------
  101. pseudo_label : List[Any]
  102. Pseudo-labels of an example (to be revised by abductive reasoning).
  103. y : Any
  104. Ground truth of the reasoning result for the example.
  105. x : List[Any]
  106. The example. If the information from the example
  107. is not required in the reasoning process, then this parameter will not have
  108. any effect.
  109. max_revision_num : int
  110. The upper limit on the number of revised labels for each example.
  111. require_more_revision : int
  112. Specifies additional number of revisions permitted beyond the minimum required.
  113. Returns
  114. -------
  115. Tuple[List[List[Any]], List[Any]]
  116. A tuple of two elements. The first element is a list of candidate revisions,
  117. i.e. revised pseudo-labels of the example. that are compatible with the knowledge
  118. base. The second element is a list of reasoning results corresponding to each
  119. candidate, i.e., the outcome of the ``logic_forward`` function.
  120. """
  121. return self._abduce_by_search(pseudo_label, y, x, max_revision_num, require_more_revision)
  122. def _check_equal(self, reasoning_result: Any, y: Any) -> bool:
  123. """
  124. Check whether the reasoning result of a pseduo label example is equal to the ground truth
  125. (or, within the maximum error allowed for numerical results).
  126. Returns
  127. -------
  128. bool
  129. The result of the check.
  130. """
  131. if reasoning_result is None:
  132. return False
  133. if isinstance(reasoning_result, (int, float)) and isinstance(y, (int, float)):
  134. return abs(reasoning_result - y) <= self.max_err
  135. else:
  136. return reasoning_result == y
  137. def revise_at_idx(
  138. self,
  139. pseudo_label: List[Any],
  140. y: Any,
  141. x: List[Any],
  142. revision_idx: List[int],
  143. ) -> List[List[Any]]:
  144. """
  145. Revise the pseudo-labels at specified index positions.
  146. Parameters
  147. ----------
  148. pseudo_label : List[Any]
  149. Pseudo-labels of an example (to be revised).
  150. y : Any
  151. Ground truth of the reasoning result for the example.
  152. x : List[Any]
  153. The example. If the information from the example
  154. is not required in the reasoning process, then this parameter will not have
  155. any effect.
  156. revision_idx : List[int]
  157. A list specifying indices of where revisions should be made to the pseudo-labels.
  158. Returns
  159. -------
  160. Tuple[List[List[Any]], List[Any]]
  161. A tuple of two elements. The first element is a list of candidate revisions,
  162. i.e. revised pseudo-labels of the example. that are compatible with the knowledge
  163. base. The second element is a list of reasoning results corresponding to each
  164. candidate, i.e., the outcome of the ``logic_forward`` function.
  165. """
  166. candidates, reasoning_results = [], []
  167. abduce_c = product(self.pseudo_label_list, repeat=len(revision_idx))
  168. for c in abduce_c:
  169. candidate = pseudo_label.copy()
  170. for i, idx in enumerate(revision_idx):
  171. candidate[idx] = c[i]
  172. reasoning_result = self.logic_forward(candidate, *(x,) if self._num_args == 2 else ())
  173. if self._check_equal(reasoning_result, y):
  174. candidates.append(candidate)
  175. reasoning_results.append(reasoning_result)
  176. return candidates, reasoning_results
  177. def _revision(
  178. self,
  179. revision_num: int,
  180. pseudo_label: List[Any],
  181. y: Any,
  182. x: List[Any],
  183. ) -> List[List[Any]]:
  184. """
  185. For a specified number of labels in an example's pseudo-labels to revise, iterate through
  186. all possible indices to find any candidates that are compatible with the knowledge base.
  187. """
  188. new_candidates, new_reasoning_results = [], []
  189. revision_idx_list = combinations(range(len(pseudo_label)), revision_num)
  190. for revision_idx in revision_idx_list:
  191. candidates, reasoning_results = self.revise_at_idx(pseudo_label, y, x, revision_idx)
  192. new_candidates.extend(candidates)
  193. new_reasoning_results.extend(reasoning_results)
  194. return new_candidates, new_reasoning_results
  195. @abl_cache()
  196. def _abduce_by_search(
  197. self,
  198. pseudo_label: List[Any],
  199. y: Any,
  200. x: List[Any],
  201. max_revision_num: int,
  202. require_more_revision: int,
  203. ) -> List[List[Any]]:
  204. """
  205. Perform abductive reasoning by exhaustive search. Specifically, begin with 0 and
  206. continuously increase the number of labels to revise, until
  207. candidates that are compatible with the knowledge base are found.
  208. Parameters
  209. ----------
  210. pseudo_label : List[Any]
  211. Pseudo-labels of an example (to be revised).
  212. y : Any
  213. Ground truth of the reasoning result for the example.
  214. x : List[Any]
  215. The example. If the information from the example
  216. is not required in the reasoning process, then this parameter will not have
  217. any effect.
  218. max_revision_num : int
  219. The upper limit on the number of revisions.
  220. require_more_revision : int
  221. If larger than 0, then after having found any candidates compatible with the
  222. knowledge base, continue to increase the number of labels to
  223. revise to get more possible compatible candidates.
  224. Returns
  225. -------
  226. Tuple[List[List[Any]], List[Any]]
  227. A tuple of two elements. The first element is a list of candidate revisions,
  228. i.e. revised pseudo-labels of the example. that are compatible with the knowledge
  229. base. The second element is a list of reasoning results corresponding to each
  230. candidate, i.e., the outcome of the ``logic_forward`` function.
  231. """
  232. candidates, reasoning_results = [], []
  233. for revision_num in range(len(pseudo_label) + 1):
  234. new_candidates, new_reasoning_results = self._revision(revision_num, pseudo_label, y, x)
  235. candidates.extend(new_candidates)
  236. reasoning_results.extend(new_reasoning_results)
  237. if len(candidates) > 0:
  238. min_revision_num = revision_num
  239. break
  240. if revision_num >= max_revision_num:
  241. return [], []
  242. for revision_num in range(
  243. min_revision_num + 1, min_revision_num + require_more_revision + 1
  244. ):
  245. if revision_num > max_revision_num:
  246. return candidates, reasoning_results
  247. new_candidates, new_reasoning_results = self._revision(revision_num, pseudo_label, y, x)
  248. candidates.extend(new_candidates)
  249. reasoning_results.extend(new_reasoning_results)
  250. return candidates, reasoning_results
  251. def __repr__(self):
  252. return (
  253. f"{self.__class__.__name__} is a KB with "
  254. f"pseudo_label_list={self.pseudo_label_list!r}, "
  255. f"max_err={self.max_err!r}, "
  256. f"use_cache={self.use_cache!r}."
  257. )
  258. class GroundKB(KBBase):
  259. """
  260. Knowledge base with a ground KB (GKB). Ground KB is a knowledge base prebuilt upon
  261. class initialization, storing all potential candidates along with their respective
  262. reasoning result. Ground KB can accelerate abductive reasoning in ``abduce_candidates``.
  263. Parameters
  264. ----------
  265. pseudo_label_list : List[Any]
  266. Refer to class ``KBBase``.
  267. GKB_len_list : List[int]
  268. List of possible lengths for pseudo-labels of an example.
  269. max_err : float, optional
  270. Refer to class ``KBBase``.
  271. Notes
  272. -----
  273. Users can also inherit from this class to build their own knowledge base. Similar
  274. to ``KBBase``, users are only required to provide the ``pseudo_label_list`` and override
  275. the ``logic_forward`` function. Additionally, users should provide the ``GKB_len_list``.
  276. After that, other operations (e.g. auto-construction of GKB, and how to perform
  277. abductive reasoning) will be automatically set up.
  278. """
  279. def __init__(
  280. self,
  281. pseudo_label_list: List[Any],
  282. GKB_len_list: List[int],
  283. max_err: float = 1e-10,
  284. ):
  285. super().__init__(pseudo_label_list, max_err)
  286. if not isinstance(GKB_len_list, list):
  287. raise TypeError("GKB_len_list should be list, but got {type(GKB_len_list)}")
  288. if self._num_args == 2:
  289. raise NotImplementedError(
  290. "GroundKB only supports 1-argument logic_forward, but got "
  291. + f"{self._num_args}-argument logic_forward"
  292. )
  293. self.GKB_len_list = GKB_len_list
  294. self.GKB = {}
  295. X, Y = self._get_GKB()
  296. for x, y in zip(X, Y):
  297. self.GKB.setdefault(len(x), defaultdict(list))[y].append(x)
  298. def _get_XY_list(self, args):
  299. pre_x, post_x_it = args[0], args[1]
  300. XY_list = []
  301. for post_x in post_x_it:
  302. x = (pre_x,) + post_x
  303. y = self.logic_forward(x)
  304. if y is not None:
  305. XY_list.append((x, y))
  306. return XY_list
  307. def _get_GKB(self):
  308. """
  309. Prebuild the GKB according to ``pseudo_label_list`` and ``GKB_len_list``.
  310. """
  311. X, Y = [], []
  312. for length in self.GKB_len_list:
  313. arg_list = []
  314. for pre_x in self.pseudo_label_list:
  315. post_x_it = product(self.pseudo_label_list, repeat=length - 1)
  316. arg_list.append((pre_x, post_x_it))
  317. with Pool(processes=len(arg_list)) as pool:
  318. ret_list = pool.map(self._get_XY_list, arg_list)
  319. for XY_list in ret_list:
  320. if len(XY_list) == 0:
  321. continue
  322. part_X, part_Y = zip(*XY_list)
  323. X.extend(part_X)
  324. Y.extend(part_Y)
  325. if Y and isinstance(Y[0], (int, float)):
  326. X, Y = zip(*sorted(zip(X, Y), key=lambda pair: pair[1]))
  327. return X, Y
  328. def abduce_candidates(
  329. self,
  330. pseudo_label: List[Any],
  331. y: Any,
  332. x: List[Any],
  333. max_revision_num: int,
  334. require_more_revision: int,
  335. ) -> List[List[Any]]:
  336. """
  337. Perform abductive reasoning by directly retrieving compatible candidates from
  338. the prebuilt GKB. In this way, the time-consuming exhaustive search can be
  339. avoided.
  340. Parameters
  341. ----------
  342. pseudo_label : List[Any]
  343. Pseudo-labels of an example (to be revised by abductive reasoning).
  344. y : Any
  345. Ground truth of the reasoning result for the example.
  346. x : List[Any]
  347. The example (unused in GroundKB).
  348. max_revision_num : int
  349. The upper limit on the number of revised labels for each example.
  350. require_more_revision : int
  351. Specifies additional number of revisions permitted beyond the minimum required.
  352. Returns
  353. -------
  354. Tuple[List[List[Any]], List[Any]]
  355. A tuple of two elements. The first element is a list of candidate revisions,
  356. i.e. revised pseudo-labels of the example. that are compatible with the knowledge
  357. base. The second element is a list of reasoning results corresponding to each
  358. candidate, i.e., the outcome of the ``logic_forward`` function.
  359. """
  360. if self.GKB == {} or len(pseudo_label) not in self.GKB_len_list:
  361. return [], []
  362. all_candidates, all_reasoning_results = self._find_candidate_GKB(pseudo_label, y)
  363. if len(all_candidates) == 0:
  364. return [], []
  365. cost_list = hamming_dist(pseudo_label, all_candidates)
  366. min_revision_num = np.min(cost_list)
  367. revision_num = min(max_revision_num, min_revision_num + require_more_revision)
  368. idxs = np.where(cost_list <= revision_num)[0]
  369. candidates = [all_candidates[idx] for idx in idxs]
  370. reasoning_results = [all_reasoning_results[idx] for idx in idxs]
  371. return candidates, reasoning_results
  372. def _find_candidate_GKB(self, pseudo_label: List[Any], y: Any) -> List[List[Any]]:
  373. """
  374. Retrieve compatible candidates from the prebuilt GKB. For numerical reasoning results,
  375. return all candidates and their corresponding reasoning results which fall within the
  376. [y - max_err, y + max_err] range.
  377. """
  378. if isinstance(y, (int, float)):
  379. potential_candidates = self.GKB[len(pseudo_label)]
  380. key_list = list(potential_candidates.keys())
  381. low_key = bisect.bisect_left(key_list, y - self.max_err)
  382. high_key = bisect.bisect_right(key_list, y + self.max_err)
  383. all_candidates, all_reasoning_results = [], []
  384. for key in key_list[low_key:high_key]:
  385. for candidate in potential_candidates[key]:
  386. all_candidates.append(candidate)
  387. all_reasoning_results.append(key)
  388. else:
  389. all_candidates = self.GKB[len(pseudo_label)][y]
  390. all_reasoning_results = [y] * len(all_candidates)
  391. return all_candidates, all_reasoning_results
  392. def __repr__(self):
  393. GKB_info_parts = []
  394. for i in self.GKB_len_list:
  395. num_candidates = len(self.GKB[i]) if i in self.GKB else 0
  396. GKB_info_parts.append(f"{num_candidates} candidates of length {i}")
  397. GKB_info = ", ".join(GKB_info_parts)
  398. return (
  399. f"{self.__class__.__name__} is a KB with "
  400. f"pseudo_label_list={self.pseudo_label_list!r}, "
  401. f"max_err={self.max_err!r}, "
  402. f"use_cache={self.use_cache!r}. "
  403. f"It has a prebuilt GKB with "
  404. f"GKB_len_list={self.GKB_len_list!r}, "
  405. f"and there are "
  406. f"{GKB_info}"
  407. f" in the GKB."
  408. )
  409. class PrologKB(KBBase):
  410. """
  411. Knowledge base provided by a Prolog (.pl) file.
  412. Parameters
  413. ----------
  414. pseudo_label_list : List[Any]
  415. Refer to class ``KBBase``.
  416. pl_file : str
  417. Prolog file containing the KB.
  418. Notes
  419. -----
  420. Users can instantiate this class to build their own knowledge base. During the
  421. instantiation, users are only required to provide the ``pseudo_label_list`` and ``pl_file``.
  422. To use the default logic forward and abductive reasoning methods in this class, in the
  423. Prolog (.pl) file, there needs to be a rule which is strictly formatted as
  424. ``logic_forward(Pseudo_labels, Res).``, e.g., ``logic_forward([A,B], C) :- C is A+B``.
  425. For specifics, refer to the ``logic_forward`` and ``get_query_string`` functions in this
  426. class. Users are also welcome to override related functions for more flexible support.
  427. """
  428. def __init__(self, pseudo_label_list: List[Any], pl_file: str):
  429. super().__init__(pseudo_label_list)
  430. try:
  431. import pyswip
  432. except (IndexError, ImportError):
  433. print(
  434. "A Prolog-based knowledge base is in use. Please install SWI-Prolog using the"
  435. + "command 'sudo apt-get install swi-prolog' for Linux users, or download it "
  436. + "following the guide in https://github.com/yuce/pyswip/blob/master/INSTALL.md "
  437. + "for Windows and Mac users."
  438. )
  439. self.prolog = pyswip.Prolog()
  440. self.pl_file = pl_file
  441. if not os.path.exists(self.pl_file):
  442. raise FileNotFoundError(f"The Prolog file {self.pl_file} does not exist.")
  443. self.prolog.consult(self.pl_file)
  444. def logic_forward(self, pseudo_label: List[Any]) -> Any:
  445. """
  446. Consult prolog with the query ``logic_forward(pseudo_labels, Res).``, and set the
  447. returned ``Res`` as the reasoning results. To use this default function, there must be
  448. a ``logic_forward`` method in the pl file to perform reasoning.
  449. Otherwise, users would override this function.
  450. Parameters
  451. ----------
  452. pseudo_label : List[Any]
  453. Pseudo-labels of an example.
  454. """
  455. result = list(self.prolog.query("logic_forward(%s, Res)." % pseudo_label))[0]["Res"]
  456. if result == "true":
  457. return True
  458. elif result == "false":
  459. return False
  460. return result
  461. def _revision_pseudo_label(
  462. self,
  463. pseudo_label: List[Any],
  464. revision_idx: List[int],
  465. ) -> List[Any]:
  466. import re
  467. revision_pseudo_label = pseudo_label.copy()
  468. revision_pseudo_label = flatten(revision_pseudo_label)
  469. for idx in revision_idx:
  470. revision_pseudo_label[idx] = "P" + str(idx)
  471. revision_pseudo_label = reform_list(revision_pseudo_label, pseudo_label)
  472. regex = r"'P\d+'"
  473. return re.sub(regex, lambda x: x.group().replace("'", ""), str(revision_pseudo_label))
  474. def get_query_string(
  475. self,
  476. pseudo_label: List[Any],
  477. y: Any,
  478. x: List[Any],
  479. revision_idx: List[int],
  480. ) -> str:
  481. """
  482. Get the query to be used for consulting Prolog.
  483. This is a default function for demo, users would override this function to adapt to
  484. their own Prolog file. In this demo function, return query
  485. ``logic_forward([kept_labels, Revise_labels], Res).``.
  486. Parameters
  487. ----------
  488. pseudo_label : List[Any]
  489. Pseudo-labels of an example (to be revised by abductive reasoning).
  490. y : Any
  491. Ground truth of the reasoning result for the example.
  492. x : List[Any]
  493. The corresponding input example. If the information from the input
  494. is not required in the reasoning process, then this parameter will not have
  495. any effect.
  496. revision_idx : List[int]
  497. A list specifying indices of where revisions should be made to the pseudo-labels.
  498. Returns
  499. -------
  500. str
  501. A string of the query.
  502. """
  503. query_string = "logic_forward("
  504. query_string += self._revision_pseudo_label(pseudo_label, revision_idx)
  505. key_is_none_flag = y is None or (isinstance(y, list) and y[0] is None)
  506. query_string += ",%s)." % y if not key_is_none_flag else ")."
  507. return query_string
  508. def revise_at_idx(
  509. self,
  510. pseudo_label: List[Any],
  511. y: Any,
  512. x: List[Any],
  513. revision_idx: List[int],
  514. ) -> List[List[Any]]:
  515. """
  516. Revise the pseudo-labels at specified index positions by querying Prolog.
  517. Parameters
  518. ----------
  519. pseudo_label : List[Any]
  520. Pseudo-labels of an example (to be revised).
  521. y : Any
  522. Ground truth of the reasoning result for the example.
  523. x : List[Any]
  524. The corresponding input example. If the information from the input
  525. is not required in the reasoning process, then this parameter will not have
  526. any effect.
  527. revision_idx : List[int]
  528. A list specifying indices of where revisions should be made to the pseudo-labels.
  529. Returns
  530. -------
  531. Tuple[List[List[Any]], List[Any]]
  532. A tuple of two elements. The first element is a list of candidate revisions,
  533. i.e. revised pseudo-labels of the example. that are compatible with the knowledge
  534. base. The second element is a list of reasoning results corresponding to each
  535. candidate, i.e., the outcome of the ``logic_forward`` function.
  536. """
  537. candidates, reasoning_results = [], []
  538. query_string = self.get_query_string(pseudo_label, y, x, revision_idx)
  539. save_pseudo_label = pseudo_label
  540. pseudo_label = flatten(pseudo_label)
  541. abduce_c = [list(z.values()) for z in self.prolog.query(query_string)]
  542. for c in abduce_c:
  543. candidate = pseudo_label.copy()
  544. for i, idx in enumerate(revision_idx):
  545. candidate[idx] = c[i]
  546. candidate = reform_list(candidate, save_pseudo_label)
  547. candidates.append(candidate)
  548. reasoning_results.append(y)
  549. return candidates, reasoning_results
  550. def __repr__(self):
  551. return (
  552. f"{self.__class__.__name__} is a KB with "
  553. f"pseudo_label_list={self.pseudo_label_list!r}, "
  554. f"defined by "
  555. f"Prolog file {self.pl_file!r}."
  556. )

An efficient Python toolkit for Abductive Learning (ABL), a novel paradigm that integrates machine learning and logical reasoning in a unified framework.