1

I would expect that the following should always be true if a comparison backtrack, right? unless it goes into an infinite loop!

 ?- Y=2 , random:random(1,3,X), X =\= Y.
 Y = 2,
 X = 1.

 ?- Y=2 , random:random(1,3,X), X =\= Y.
 false. 

but I got false! In general, my question is why doesn't comparison backtrack?


Thanks for all the answers. My confusion seemed to come primarily from my expectation of random keep generating new-random numbers, so I confused that comparison was not backtracking, instead, the reason was that random does its thing only once and fails afterwards. I was unaware of semi-determinate nature of some predicates. But now I can be on a lookout ;) for cases like this. thanks again.

sten
  • 7,028
  • 9
  • 41
  • 63
  • Friendly reminder to accept an answer for this question if it was answered satisfactory for you, or to ask for further clarification if you think an answer can be improved – G_V Mar 02 '18 at 11:14

3 Answers3

5

In your example, there is nothing to backtrack.

All predicates you are using in these examples ((=)/2, random/3 and (=\=)/2) are semi-deterministic: This means that they either fail, or succeed exactly once.

In other words, they can all succeed at most once.

Therefore, if at least one of these predicates fails, then the query fails.

To generate a succession of pseudo-random numbers on backtracking, use for example repeat/0.

Warning: random/3 is an impure predicate: It may yield different solutions even though the query is exactly the same. It may lead to failure on one invocation, and to success on another. This complicates testing and reasoning about your code considerably.

mat
  • 40,498
  • 3
  • 51
  • 78
  • i see, this make sense.. i didn't know that there are predicates that don't backtrack. – sten Feb 21 '18 at 22:45
  • I think this is generally a good sign: You are apparently reasoning about your programs as if the predicates you are using were true relations. However, not all Prolog predicates are true relations, and `random/3` in particular cannot be reasoned about in this way. – mat Feb 21 '18 at 22:48
  • why is comparison semi-det. isn't it possible for the variables to be reunificated with different value on backtracking i.e. the need to do comparison with the new values ? – sten Feb 21 '18 at 22:51
  • 2
    The comparison either succeeds or fails, there are no other possibilities. If there are different values, they need to come from somewhere: A different predicate that describes them and generates them on backtracking. However, I recommend to think more declaratively about your programs: If you are trying to understand the exact execution sequence, you will not get very far, because it quickly becomes much too complex to understand. Better think in terms of what holds and what ought to hold, and about the relations between things. – mat Feb 21 '18 at 22:57
  • oo i see , u are saying if it wasn't random/3 but some clauses that generated new numbers on backtracking the comparison will also backtrack. of course u have to be careful of what you are doing. – sten Feb 21 '18 at 23:05
  • 2
    The comparison can only fail or succeed! Any repeated invocation must be due to other goals that are invoked before the comparison. However, again, I recommend you think more declaratively about your code: The actual execution strategy is typically too complex to understand if you try to take low-level details into account. – mat Feb 21 '18 at 23:16
  • Does semi-det behavior holds for tail-recursive clause ? – sten Feb 26 '18 at 22:03
1

I think this will help.



    % Generate random value from Min to Max(with backtrack)
    rand_backtrack(Min,Max,RandVal):-
        create_list(Min,Max,List),
        randomize_list(List,Randomized),
        length(Randomized,Len),

    % Choose one Variable from Randomized (From first element to last). 
    % When backtrack occured, next element is chosen.
        between(1,Len,Idx),   
        nth1(Idx,Randomized,RandVal). 

    % create integer order list 
    % [Min,Min+1,Min+2,....,Max]
    create_list(Max,Max,[Max]):-!.
    create_list(Min,Max,[Min|Rest]):-
        Min1 is Min+1,
        create_list(Min1,Max,Rest).

    % shuffle List.
    % result always changes.
    % ex.randomize_list([1,2,3,4,5,6],R)     R=[4,2,6,1,3,5]
    %
    randomize_list([Val],[Val]):-!.
    randomize_list(List,[RandVal|RestRandomized]):-
        length(List,Len),
        random(1,Len,RandIdx),
        nth1(RandIdx,List,RandVal),
        select(RandVal, List, Rest),
        !,
        randomize_list(Rest,RestRandomized).


     ?- rand_backtrack(3,19,A).
    A = 6 ;
    A = 4 ;
    A = 8 ;
    A = 13 ;
    A = 15 ;
    A = 16 ;
    A = 9 ;
    A = 18 ;
    A = 7 ;
    A = 3 ;
    A = 12 ;
    A = 10 ;
    A = 17 ;
    A = 11 ;
    A = 14 ;
    A = 5 ;
    A = 19.

Taku Koyahata
  • 558
  • 2
  • 13
  • While that is very, very nice, could you add some comments to explain what you are actually doing inside the program please? The goal of logic I think is to explain universalized concepts that can be applied in many situations rather than having a single working program. For example, if you say the initiation of force is wrong, then hitting someone who did not initiate force against you, is wrong. – G_V Feb 22 '18 at 10:12
  • 1
    oh, sorry. I added simple ones. – Taku Koyahata Feb 22 '18 at 10:50
1

Prolog works with what are called Horn-clauses. This means that each term individually, for example Y=2, is a separate goal in a question to be answered. The result will be yes or no for each goal, and if all goals answer yes, the question is answered with yes.

What your code asks is as follows:

%Is Y equal to 2 or can it be made equal? 
%Yes, Y is a variable and can be assigned the numerical atom 2
Y=2 ,
%Give me a random number between 1 and 3. 
%Is X equal to the random number or can it be made equal? 
%Yes, X is a variable and can be assigned the outcome atom of random:random
random:random(1,3,X), 
%is the term contained within X NOT equivalent to Y?
X =\= Y.

You can check out existing comparison predicates in for example the SWI documentation or on Learn Prolog Now!.

Depending on your implementation you can use trace and write to output the actual atoms in the variables, allowing you to explore how your program actually works.

?- trace, (Y=2 , random:random(1,3,X), write(X),nl, X =\= Y). %SWI-Prolog

SWI-prolog online editor

Infinite recursion looks like p(P) :- p(P).. It has a call to the question it is supposed to solve inside the answer itself, meaning to solve for p(P) it will check p(P), which never ends.

Backtracking only happens when Prolog has choicepoints. Choicepoints are points where in the decision tree, there are MULTIPLE POSSIBLE WAYS to satisfy the question Prolog is currently processing. Prolog works from top to bottom, then left to right.

Think of a cars salesman who gets asked "which car is the best for me?". He has more than one possible car to sell you, so he'll start showing you different cars that meet your criteria. The car needs to have a transport capacity of a volume over 400 liters? All cars that don't satisfy this condition are not presented as a solution.

Prolog does a depth-first search, meaning it goes down to the first answer it finds, then checks whether there's other ways to answer it. If there is no result, the answer is no. If there is at least one solution, the answer is yes and you get all possible answers for your question. This way you only get results that satisfy a whole chain of goals you've set.

G_V
  • 2,396
  • 29
  • 44