/src/lib/backtracking/low_level/abstract_backtracking.e
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.