/src/lib/backtracking/low_level/abstract_backtracking.e

http://github.com/tybor/Liberty · Specman e · 485 lines · 277 code · 33 blank · 175 comment · 6 complexity · 60b373808b9ed27b1386157c0033bd48 MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. deferred class ABSTRACT_BACKTRACKING
  5. --
  6. -- This class is intended to explore structures that match the and/or
  7. -- pattern. The and/or pattern is found for example in the evaluation of
  8. -- the regular expressions, in the evaluation of Prolog queries, in
  9. -- solving some generic problem of artificial intelligence.
  10. --
  11. -- The instances of the class inheriting ABSTRACT_BACKTRACKING are
  12. -- typically used through code like the following one that enumerate
  13. -- all the solutions:
  14. --
  15. -- from
  16. -- init_exploration(explorer)
  17. -- explorer.search_first
  18. -- until
  19. -- explorer.is_off
  20. -- loop
  21. -- treat_solution(explorer)
  22. -- explorer.search_next
  23. -- end
  24. --
  25. -- The exploration is the enumeration of all the paths in an abstract
  26. -- structure made of alternatives or sequences of goals to satisfy.
  27. --
  28. -- The class ABSTRACT_BACKTRACKING does not make any assumption on
  29. -- what is explored. That is its strength because it can be used in
  30. -- many situations. In such a case, nothing is said about what
  31. -- is a solution and what is a context. The implementations usually
  32. -- have to provide it.
  33. --
  34. -- A good understanding of how that class works is required if you
  35. -- intend to use it. See also the more easy-to-use class BACKTRACKING.
  36. --
  37. -- See tutorial/backtracking for examples.
  38. --
  39. -- The deferred features are 'evaluate_current_state'. 'context_clear',
  40. -- 'context_push', 'context_restore', 'context_restore_and_pop', 'context_cut'.
  41. --
  42. -- When the feature 'evaluate_current_state' is called,
  43. -- the implementor must perform actions to process the exploration.
  44. -- Here are some of the common things that can be done (note that
  45. -- one or more of these actions can be done):
  46. -- + stand-alone actions
  47. -- - replace the current state by an other state.
  48. -- - call 'backtrack': cancel the exploration of the current
  49. -- alternative and explore the next alternative.
  50. -- - call 'continue': explore the continuation of the
  51. -- current alternative.
  52. -- + actions that can be mixed but that need to be completed by
  53. -- one of the above actions
  54. -- - create a sequence of future path to evaluate and push it
  55. -- to the continuation path by calling 'push_sequence'.
  56. -- - create an alternative of future alternate path and push it
  57. -- to the alternative stack by calling 'push_alternative'.
  58. -- - push a cut point by calling 'push_cut_point'.
  59. -- - remove all alternatives until upper cut point by calling 'cut'.
  60. -- - remove all alternatives by calling 'cut_all'.
  61. --
  62. --
  63. -- Because the exploration of and/or abstractions can be huge, that
  64. -- class requires to manage alternatives and sequences in pools.
  65. --
  66. insert
  67. ABSTRACT_BACKTRACKING_GLOBALS
  68. feature {ANY} -- Common client features
  69. search_first
  70. -- Resets all and searches the first solution.
  71. -- The current state must be set. It is the
  72. -- first state, the root of the search.
  73. -- When the feature returns, 'search_is_success' must be
  74. -- checked to know if a solution was found.
  75. -- When search_is_success=False, it means that there
  76. -- is no solution at all. Conversely, if search_is_success=True,
  77. -- then the first solution is found and 'search_next'
  78. -- can be called to get the next solution if it exists.
  79. do
  80. -- prepare search from root
  81. clear
  82. stop_search_loop := False
  83. search_is_success := True
  84. -- search
  85. search
  86. ensure
  87. success_or_off: search_is_success and not is_off or is_off and not search_is_success
  88. end
  89. search_next
  90. -- Searches the next solution.
  91. -- When the feature returns, 'search_is_success' must be
  92. -- checked to know if a solution was found.
  93. -- When search_is_success=False at the end, it means that there
  94. -- is no more solution. Conversely, if search_is_success=True,
  95. -- then a solution is found and 'search_next'
  96. -- can be called again to get the next solution.
  97. require
  98. last_search_was_a_success: search_is_success
  99. do
  100. -- prepare next search
  101. stop_search_loop := False
  102. backtrack
  103. -- search
  104. search
  105. ensure
  106. success_or_off: search_is_success and not is_off or is_off and not search_is_success
  107. end
  108. search_is_success: BOOLEAN
  109. -- True when search is successful
  110. is_off: BOOLEAN
  111. -- True when search is finished
  112. do
  113. Result := not search_is_success
  114. ensure
  115. definition: Result = not search_is_success
  116. end
  117. clear
  118. -- Clears the current state to nothing.
  119. do
  120. context_clear
  121. from
  122. current_continuation := Void
  123. until
  124. top_sequence = Void
  125. loop
  126. remove_top_sequence
  127. end
  128. from
  129. until
  130. top_alternative = Void
  131. loop
  132. remove_top_alternative
  133. end
  134. search_is_success := False
  135. ensure
  136. cleared: is_cleared
  137. no_solution: is_off
  138. end
  139. is_cleared: BOOLEAN
  140. -- True if no partial data remain in the current state
  141. do
  142. Result := top_alternative = Void and then top_sequence = Void and then is_off
  143. ensure
  144. no_solution_when_cleared: Result implies is_off
  145. end
  146. feature {ANY} -- Control of the exploration
  147. push_sequence (sequence: like top_sequence)
  148. -- Pushes the `sequence' in front of the continuation path.
  149. require
  150. sequence_not_void: sequence /= Void
  151. do
  152. sequence.set_continuation(current_continuation)
  153. sequence.set_previous(top_sequence)
  154. top_sequence := sequence
  155. current_continuation := top_sequence
  156. ensure
  157. is_on_top: top_sequence = sequence
  158. is_first_continuation: current_continuation = sequence
  159. previous_top_linked: top_sequence.previous = old top_sequence
  160. previous_continuation_linked: top_sequence.continuation = old current_continuation
  161. end
  162. push_alternative (alternative: like top_alternative)
  163. -- Pushes the `alternative' before the continuation path.
  164. require
  165. alternative_not_void: alternative /= Void
  166. do
  167. context_push
  168. alternative.set_continuation(current_continuation)
  169. alternative.set_top_sequence(top_sequence)
  170. alternative.set_previous(top_alternative)
  171. top_alternative := alternative
  172. if current_continuation /= Void then
  173. current_continuation := current_continuation.get_twin
  174. current_continuation.set_previous(top_sequence)
  175. top_sequence := current_continuation
  176. end
  177. ensure
  178. is_on_top: top_alternative = alternative
  179. previous_top_linked: top_alternative.previous = old top_alternative
  180. top_sequence_recorded: top_alternative.top_sequence = old top_sequence
  181. continuation_recorded: top_alternative.continuation = old current_continuation
  182. end
  183. continue
  184. -- Continues the exploration of the current path.
  185. do
  186. if current_continuation /= Void then
  187. current_continuation.next_sequence(Current)
  188. else
  189. stop_search_loop := True
  190. end
  191. end
  192. backtrack
  193. -- Stops the exploration of the current path and
  194. -- tries to explore the next alternative path.
  195. do
  196. if top_alternative /= Void then
  197. top_alternative.next_alternative(Current)
  198. else
  199. from
  200. until
  201. top_sequence = Void
  202. loop
  203. remove_top_sequence
  204. end
  205. current_continuation := Void
  206. stop_search_loop := True
  207. search_is_success := False
  208. end
  209. end
  210. push_cut_point
  211. -- Inserts a cut point into the continuation path.
  212. -- The inserted cut point records the current
  213. -- top of the alternatives.
  214. local
  215. cut_point: ABSTRACT_BACKTRACKING_CUT_POINT
  216. do
  217. -- creates the cut point
  218. cut_point := pool_of_cut_points.get_instance
  219. cut_point.set_top_alternative(top_alternative)
  220. push_sequence(cut_point)
  221. end
  222. cut
  223. -- Removes the alternatives until the one recorded by the next
  224. -- cut point in the continuation path.
  225. local
  226. sequence: like top_sequence; cut_point: ABSTRACT_BACKTRACKING_CUT_POINT
  227. alternative: ABSTRACT_BACKTRACKING_ALTERNATIVE
  228. do
  229. -- retrieves the first cut point in the continuation path
  230. from
  231. sequence := current_continuation
  232. until
  233. sequence = Void
  234. loop
  235. if cut_point ?:= sequence then
  236. cut_point ::= sequence
  237. sequence := sequence.continuation
  238. else
  239. alternative := cut_point.top_alternative
  240. sequence := Void
  241. end
  242. end
  243. -- removes the alternatives until the one that
  244. -- recorded in the retrieved cut point.
  245. cut_until(alternative)
  246. end
  247. cut_all
  248. -- Cuts all alternatives.
  249. do
  250. cut_until(Void)
  251. end
  252. feature {} -- Internal
  253. stop_search_loop: BOOLEAN
  254. -- True if at the end of a search.
  255. -- This occurs if either a solution is found
  256. -- (and then search_is_success=True) or no solution
  257. -- is found (and then search_is_success=False).
  258. -- That feature should be modified only by `continue'
  259. -- and `backtrack'.
  260. search
  261. -- Common search loop to search_first and search_next
  262. do
  263. from
  264. until
  265. stop_search_loop
  266. loop
  267. evaluate_current_state
  268. end
  269. ensure
  270. stop_search_loop
  271. end
  272. cut_until (alternative: ABSTRACT_BACKTRACKING_ALTERNATIVE)
  273. -- Cut all alternatives until 'alternative'.
  274. -- To cut an alternative is currently to remove it.
  275. do
  276. from
  277. until
  278. top_alternative = alternative
  279. loop
  280. context_cut
  281. remove_top_alternative
  282. end
  283. ensure
  284. definition: top_alternative = alternative
  285. end
  286. feature {} -- Internal deferred
  287. evaluate_current_state
  288. -- That feature is called to evaluate the current state.
  289. -- Called repeatedly by `search' until `stop_search_loop'.
  290. deferred
  291. end
  292. context_clear
  293. -- Clear any saved context.
  294. -- Called by the features `clear' and `search_first'.
  295. deferred
  296. end
  297. context_push
  298. -- Push the current context.
  299. -- Called each time that an alternative begins.
  300. deferred
  301. end
  302. context_restore
  303. -- Restore the context to the last saved one.
  304. -- The saved context MUST remain available for future use.
  305. -- Called each time that a new alternative (of the
  306. -- previous alternative point) is starting.
  307. deferred
  308. end
  309. context_restore_and_pop
  310. -- Restore the context to the last saved one and drop it.
  311. -- The saved context MUST be removed.
  312. -- Called each time that the last alternative (of the
  313. -- previous alternative point) is starting.
  314. -- Should be similar to `context_restore' followed by `context_cut'.
  315. deferred
  316. end
  317. context_cut
  318. -- Remove the last saved context.
  319. -- Called by `cut', `cut_all' or `cut_until'.
  320. deferred
  321. end
  322. feature {ABSTRACT_BACKTRACKING_SEQUENCE} -- Specific to sequences
  323. top_sequence: ABSTRACT_BACKTRACKING_SEQUENCE
  324. -- Stack of sequences represented by its top (can
  325. -- be Void)
  326. current_continuation: like top_sequence
  327. -- The current continuation path
  328. pop_sequence
  329. -- Removes the current sequence and replace it by
  330. -- the next sequence in the continuation path.
  331. require
  332. top_sequence /= Void
  333. current_continuation /= Void
  334. do
  335. -- technical note: because the sequences are
  336. -- free to record there iteration state, the
  337. -- continuations, that can be used several times,
  338. -- need to be copied when not Void.
  339. current_continuation := top_sequence.continuation
  340. remove_top_sequence
  341. if current_continuation /= Void then
  342. current_continuation := current_continuation.get_twin
  343. current_continuation.set_previous(top_sequence)
  344. top_sequence := current_continuation
  345. end
  346. end
  347. feature {ABSTRACT_BACKTRACKING_ALTERNATIVE} -- Specific to alternatives
  348. top_alternative: ABSTRACT_BACKTRACKING_ALTERNATIVE
  349. -- Stack of alternatives represented by its top (can
  350. -- be Void)
  351. continue_alternative
  352. -- Returns to the alternative on the top of the stack
  353. -- and put its saved continuation path as the
  354. -- current continuation path.
  355. require
  356. top_alternative /= Void
  357. do
  358. -- removes useless data
  359. from
  360. until
  361. top_sequence = top_alternative.top_sequence
  362. loop
  363. remove_top_sequence
  364. end
  365. -- restores the saved continuation path
  366. context_restore
  367. current_continuation := top_alternative.continuation
  368. if current_continuation /= Void then
  369. current_continuation := top_sequence.get_twin
  370. current_continuation.set_previous(top_sequence)
  371. top_sequence := current_continuation
  372. end
  373. ensure
  374. alternative_remain: top_alternative = old top_alternative
  375. end
  376. pop_alternative
  377. -- Returns to the alternative on the top of the stack
  378. -- and put its saved continuation path as the
  379. -- current continuation path.
  380. -- Remove the alternative from the stack of alternatives.
  381. -- Same as `continue_alternative' but also removes
  382. -- the alternative.
  383. require
  384. top_alternative /= Void
  385. do
  386. -- removes useless data
  387. from
  388. until
  389. top_sequence = top_alternative.top_sequence
  390. loop
  391. remove_top_sequence
  392. end
  393. -- restore the saved continuation path and pop the alternative
  394. context_restore_and_pop
  395. current_continuation := top_alternative.continuation
  396. remove_top_alternative
  397. ensure
  398. alternative_poped: top_alternative = old top_alternative.previous
  399. top_sequence_restored: top_sequence = old top_alternative.top_sequence
  400. continuation_restored: current_continuation = old top_alternative.continuation
  401. end
  402. feature {} -- internal: allocation and collection
  403. remove_top_sequence
  404. -- Removes the top sequence.
  405. require
  406. top_sequence /= Void
  407. local
  408. sequence: like top_sequence
  409. do
  410. sequence := top_sequence
  411. top_sequence := sequence.previous
  412. sequence.release
  413. ensure
  414. top_sequence = old top_sequence.previous
  415. end
  416. remove_top_alternative
  417. -- Removes the top alternative.
  418. require
  419. top_alternative /= Void
  420. local
  421. alternative: like top_alternative
  422. do
  423. alternative := top_alternative
  424. top_alternative := alternative.previous
  425. alternative.release
  426. ensure
  427. top_alternative = old top_alternative.previous
  428. end
  429. invariant
  430. current_continuation = Void or current_continuation = top_sequence
  431. top_sequence = Void implies current_continuation = Void
  432. end -- class ABSTRACT_BACKTRACKING
  433. --
  434. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  435. --
  436. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  437. -- of this software and associated documentation files (the "Software"), to deal
  438. -- in the Software without restriction, including without limitation the rights
  439. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  440. -- copies of the Software, and to permit persons to whom the Software is
  441. -- furnished to do so, subject to the following conditions:
  442. --
  443. -- The above copyright notice and this permission notice shall be included in
  444. -- all copies or substantial portions of the Software.
  445. --
  446. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  447. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  448. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  449. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  450. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  451. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  452. -- THE SOFTWARE.