PageRenderTime 27ms CodeModel.GetById 16ms app.highlight 6ms RepoModel.GetById 2ms app.codeStats 0ms

/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--
  4deferred 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
 67insert
 68   ABSTRACT_BACKTRACKING_GLOBALS
 69
 70feature {ANY} -- Common client features
 71   search_first
 72         -- Resets all and searches the first solution.
 73         -- The current state must be set. It is the
 74         -- first state, the root of the search.
 75         -- When the feature returns, 'search_is_success' must be
 76         -- checked to know if a solution was found.
 77         -- When search_is_success=False, it means that there
 78         -- is no solution at all. Conversely, if search_is_success=True,
 79         -- then the first solution is found and 'search_next'
 80         -- can be called to get the next solution if it exists.
 81      do
 82         -- prepare search from root
 83         clear
 84         stop_search_loop := False
 85         search_is_success := True
 86         -- search
 87         search
 88      ensure
 89         success_or_off: search_is_success and not is_off or is_off and not search_is_success
 90      end
 91
 92   search_next
 93         -- Searches the next solution.
 94         -- When the feature returns, 'search_is_success' must be
 95         -- checked to know if a solution was found.
 96         -- When search_is_success=False at the end, it means that there
 97         -- is no more solution. Conversely, if search_is_success=True,
 98         -- then a solution is found and 'search_next'
 99         -- can be called again to get the next solution.
100      require
101         last_search_was_a_success: search_is_success
102      do
103         -- prepare next search
104         stop_search_loop := False
105         backtrack
106         -- search
107         search
108      ensure
109         success_or_off: search_is_success and not is_off or is_off and not search_is_success
110      end
111
112   search_is_success: BOOLEAN
113         -- True when search is successful
114
115   is_off: BOOLEAN
116         -- True when search is finished
117      do
118         Result := not search_is_success
119      ensure
120         definition: Result = not search_is_success
121      end
122
123   clear
124         -- Clears the current state to nothing.
125      do
126         context_clear
127         from
128            current_continuation := Void
129         until
130            top_sequence = Void
131         loop
132            remove_top_sequence
133         end
134         from
135         until
136            top_alternative = Void
137         loop
138            remove_top_alternative
139         end
140         search_is_success := False
141      ensure
142         cleared: is_cleared
143         no_solution: is_off
144      end
145
146   is_cleared: BOOLEAN
147         -- True if no partial data remain in the current state
148      do
149         Result := top_alternative = Void and then top_sequence = Void and then is_off
150      ensure
151         no_solution_when_cleared: Result implies is_off
152      end
153
154feature {ANY} -- Control of the exploration
155   push_sequence (sequence: like top_sequence)
156         -- Pushes the `sequence' in front of the continuation path.
157      require
158         sequence_not_void: sequence /= Void
159      do
160         sequence.set_continuation(current_continuation)
161         sequence.set_previous(top_sequence)
162         top_sequence := sequence
163         current_continuation := top_sequence
164      ensure
165         is_on_top: top_sequence = sequence
166         is_first_continuation: current_continuation = sequence
167         previous_top_linked: top_sequence.previous = old top_sequence
168         previous_continuation_linked: top_sequence.continuation = old current_continuation
169      end
170
171   push_alternative (alternative: like top_alternative)
172         -- Pushes the `alternative' before the continuation path.
173      require
174         alternative_not_void: alternative /= Void
175      do
176         context_push
177         alternative.set_continuation(current_continuation)
178         alternative.set_top_sequence(top_sequence)
179         alternative.set_previous(top_alternative)
180         top_alternative := alternative
181         if current_continuation /= Void then
182            current_continuation := current_continuation.get_twin
183            current_continuation.set_previous(top_sequence)
184            top_sequence := current_continuation
185         end
186      ensure
187         is_on_top: top_alternative = alternative
188         previous_top_linked: top_alternative.previous = old top_alternative
189         top_sequence_recorded: top_alternative.top_sequence = old top_sequence
190         continuation_recorded: top_alternative.continuation = old current_continuation
191      end
192
193   continue
194         -- Continues the exploration of the current path.
195      do
196         if current_continuation /= Void then
197            current_continuation.next_sequence(Current)
198         else
199            stop_search_loop := True
200         end
201      end
202
203   backtrack
204         -- Stops the exploration of the current path and
205         -- tries to explore the next alternative path.
206      do
207         if top_alternative /= Void then
208            top_alternative.next_alternative(Current)
209         else
210            from
211            until
212               top_sequence = Void
213            loop
214               remove_top_sequence
215            end
216            current_continuation := Void
217            stop_search_loop := True
218            search_is_success := False
219         end
220      end
221
222   push_cut_point
223         -- Inserts a cut point into the continuation path.
224         -- The inserted cut point records the current
225         -- top of the alternatives.
226      local
227         cut_point: ABSTRACT_BACKTRACKING_CUT_POINT
228      do
229         -- creates the cut point
230         cut_point := pool_of_cut_points.get_instance
231         cut_point.set_top_alternative(top_alternative)
232         push_sequence(cut_point)
233      end
234
235   cut
236         -- Removes the alternatives until the one recorded by the next
237         -- cut point in the continuation path.
238      local
239         sequence: like top_sequence; cut_point: ABSTRACT_BACKTRACKING_CUT_POINT
240         alternative: ABSTRACT_BACKTRACKING_ALTERNATIVE
241      do
242         -- retrieves the first cut point in the continuation path
243         from
244            sequence := current_continuation
245         until
246            sequence = Void
247         loop
248            if cut_point ?:= sequence then
249               cut_point ::= sequence
250               sequence := sequence.continuation
251            else
252               alternative := cut_point.top_alternative
253               sequence := Void
254            end
255         end
256         -- removes the alternatives until the one that
257         -- recorded in the retrieved cut point.
258         cut_until(alternative)
259      end
260
261   cut_all
262         -- Cuts all alternatives.
263      do
264         cut_until(Void)
265      end
266
267feature {} -- Internal
268   stop_search_loop: BOOLEAN
269         -- True if at the end of a search.
270         -- This occurs if either a solution is found
271         -- (and then search_is_success=True) or no solution
272         -- is found (and then search_is_success=False).
273         -- That feature should be modified only by `continue'
274         -- and `backtrack'.
275
276   search
277         -- Common search loop to search_first and search_next
278      do
279         from
280         until
281            stop_search_loop
282         loop
283            evaluate_current_state
284         end
285      ensure
286         stop_search_loop
287      end
288
289   cut_until (alternative: ABSTRACT_BACKTRACKING_ALTERNATIVE)
290         -- Cut all alternatives until 'alternative'.
291         -- To cut an alternative is currently to remove it.
292      do
293         from
294         until
295            top_alternative = alternative
296         loop
297            context_cut
298            remove_top_alternative
299         end
300      ensure
301         definition: top_alternative = alternative
302      end
303
304feature {} -- Internal deferred
305   evaluate_current_state
306         -- That feature is called to evaluate the current state.
307         -- Called repeatedly by `search' until `stop_search_loop'.
308      deferred
309      end
310
311   context_clear
312         -- Clear any saved context.
313         -- Called by the features `clear' and `search_first'.
314      deferred
315      end
316
317   context_push
318         -- Push the current context.
319         -- Called each time that an alternative begins.
320      deferred
321      end
322
323   context_restore
324         -- Restore the context to the last saved one.
325         -- The saved context MUST remain available for future use.
326         -- Called each time that a new alternative (of the
327         -- previous alternative point) is starting.
328      deferred
329      end
330
331   context_restore_and_pop
332         -- Restore the context to the last saved one and drop it.
333         -- The saved context MUST be removed.
334         -- Called each time that the last alternative (of the
335         -- previous alternative point) is starting.
336         -- Should be similar to `context_restore' followed by `context_cut'.
337      deferred
338      end
339
340   context_cut
341         -- Remove the last saved context.
342         -- Called by `cut', `cut_all' or `cut_until'.
343      deferred
344      end
345
346feature {ABSTRACT_BACKTRACKING_SEQUENCE} -- Specific to sequences
347   top_sequence: ABSTRACT_BACKTRACKING_SEQUENCE
348         -- Stack of sequences represented by its top (can
349         -- be Void)
350
351   current_continuation: like top_sequence
352         -- The current continuation path
353
354   pop_sequence
355         -- Removes the current sequence and replace it by
356         -- the next sequence in the continuation path.
357      require
358         top_sequence /= Void
359         current_continuation /= Void
360      do
361         -- technical note: because the sequences are
362         -- free to record there iteration state, the
363         -- continuations, that can be used several times,
364         -- need to be copied when not Void.
365         current_continuation := top_sequence.continuation
366         remove_top_sequence
367         if current_continuation /= Void then
368            current_continuation := current_continuation.get_twin
369            current_continuation.set_previous(top_sequence)
370            top_sequence := current_continuation
371         end
372      end
373
374feature {ABSTRACT_BACKTRACKING_ALTERNATIVE} -- Specific to alternatives
375   top_alternative: ABSTRACT_BACKTRACKING_ALTERNATIVE
376         -- Stack of alternatives represented by its top (can
377         -- be Void)
378
379   continue_alternative
380         -- Returns to the alternative on the top of the stack
381         -- and put its saved continuation path as the
382         -- current continuation path.
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         -- restores the saved continuation path
394         context_restore
395         current_continuation := top_alternative.continuation
396         if current_continuation /= Void then
397            current_continuation := top_sequence.get_twin
398            current_continuation.set_previous(top_sequence)
399            top_sequence := current_continuation
400         end
401      ensure
402         alternative_remain: top_alternative = old top_alternative
403      end
404
405   pop_alternative
406         -- Returns to the alternative on the top of the stack
407         -- and put its saved continuation path as the
408         -- current continuation path.
409         -- Remove the alternative from the stack of alternatives.
410         -- Same as `continue_alternative' but also removes
411         -- the alternative.
412      require
413         top_alternative /= Void
414      do
415         -- removes useless data
416         from
417         until
418            top_sequence = top_alternative.top_sequence
419         loop
420            remove_top_sequence
421         end
422         -- restore the saved continuation path and pop the alternative
423         context_restore_and_pop
424         current_continuation := top_alternative.continuation
425         remove_top_alternative
426      ensure
427         alternative_poped: top_alternative = old top_alternative.previous
428         top_sequence_restored: top_sequence = old top_alternative.top_sequence
429         continuation_restored: current_continuation = old top_alternative.continuation
430      end
431
432feature {} -- internal: allocation and collection
433   remove_top_sequence
434         -- Removes the top sequence.
435      require
436         top_sequence /= Void
437      local
438         sequence: like top_sequence
439      do
440         sequence := top_sequence
441         top_sequence := sequence.previous
442         sequence.release
443      ensure
444         top_sequence = old top_sequence.previous
445      end
446
447   remove_top_alternative
448         -- Removes the top alternative.
449      require
450         top_alternative /= Void
451      local
452         alternative: like top_alternative
453      do
454         alternative := top_alternative
455         top_alternative := alternative.previous
456         alternative.release
457      ensure
458         top_alternative = old top_alternative.previous
459      end
460
461invariant
462   current_continuation = Void or current_continuation = top_sequence
463   top_sequence = Void implies current_continuation = Void
464
465end -- class ABSTRACT_BACKTRACKING
466--
467-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
468--
469-- Permission is hereby granted, free of charge, to any person obtaining a copy
470-- of this software and associated documentation files (the "Software"), to deal
471-- in the Software without restriction, including without limitation the rights
472-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
473-- copies of the Software, and to permit persons to whom the Software is
474-- furnished to do so, subject to the following conditions:
475--
476-- The above copyright notice and this permission notice shall be included in
477-- all copies or substantial portions of the Software.
478--
479-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
480-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
481-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
482-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
483-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
484-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
485-- THE SOFTWARE.