I'm trying to write something like "you have the ball if you previously got the ball, and didn't give it since":
:- use_module(library(clpfd)).
time(T1, has_ball) :-
time(T2, get_ball),
T2 #=< T1,
\+ (time(T3, give_ball),
T2 #< T3, T3 #< T1).
time(0, get_ball).
time(2, give_ball).
This correctly answers direct questions about a specific time T (by providing T, for example with labeling):
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
false.
But when asked to find all valid times T, I simply get false:
?- time(T, has_ball).
false.
From my understanding, clpfd did some over approximation of the result without labeling, so I would have expected something like "T in inf..sup, time(T, has_ball)." telling me to use labeling. But it is clear that I am wrong, and now I am afraid I might miss solutions in other situations. Can someone help me understand?
EDIT:
Isabelle Newbie's answer made me realize that I actually meant:
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
\+ (time(GiveBall, give_ball),
GetBall #< GiveBall #/\ GiveBall #< HasBall).
Since the idea is that "you still have the ball at time HasBall if you got it at some previous time GetBall, and did NOT give it SINCE". So the time(GiveBall, give_ball) needs to be in the negation. Here replacing \+ with #\ gives a new error ("Domain error: `clpfd_reifiable_expression' expected") which I will investigate.
The short answer is that you shouldn't mix Prolog negation \+ with CLP(FD). CLP(FD) has its own negation operator that works on its constraints, written as #\. So you can write your predicate as:
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
time(GiveBall, give_ball),
#\ (GetBall #< GiveBall #/\ GiveBall #< HasBall).
I renamed your variables because I didn't really understand what was going on. It's a bit clearer now, but shouldn't the negated constraint just be replaced by the positive HasBall #=< GiveBall?
In any case this behaves as I believe you would like it to:
?- time(T, has_ball).
T in 0..2.
?- time(T, has_ball), label([T]).
T = 0 ;
T = 1 ;
T = 2.
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
false.
To understand a bit more what was going on, we can take your original clause and replace variables by their constant values:
step1(T1) :-
T2 = 0,
T2 #=< T1,
\+ ( T3 = 2, T2 #< T3, T3 #< T1 ).
step2(T1) :-
0 #=< T1,
\+ ( 0 #< 2, 2 #< T1 ).
step3(T1) :-
0 #=< T1,
\+ ( 2 #< T1 ).
So after the last step, your predicate behaves essentially as follows when called with a bound vs. an unbound variable:
?- T1 = 1, 0 #=< T1, \+ (2 #< T1).
T1 = 1.
?- 0 #=< T1, \+ (2 #< T1).
false.
This is because in the first case the last goal is \+ (2 #< 1), which succeeds because 2 #< 1 fails.
But if you don't bind T1, then 2 #< T1 succeeds:
?- 2 #< T1.
T1 in 3..sup.
So its negation \+ (2 #< T1) fails. This goal essentially says "there are no numbers larger than two", which is false. In contrast, the CLP(FD) negation succeeds with the "opposite" constraint:
?- #\ (2 #< T1).
T1 in inf..2.
This almost certainly makes more sense in the context of your program, since it respects the mathematical property that not (A < B) is equivalent to (A >= B):
?- 2 #>= T1.
T1 in inf..2.
EDIT: I missed the fact that maybe no give_ball event has been seen, in which case one would still hold the ball. You can't use #\ to model this the way you tried because #\ applies only to CLP(FD) constraints (specifically, "reifiable" ones) but not to "normal" Prolog goals. You can't mix these levels this way either.
So you need to be more explicit about the two cases that exist: You have not given up the ball if either:
the ball is given up at a certain time, but that time has not arrived yet; or
the ball is not given up at all.
Here is the same in Prolog, separating the places where Prolog negation is applied from where CLP(FD) negation is applied:
has_not_given_up_ball(HasBall) :-
time(GiveBall, give_ball),
\# ( GetBall #< GiveBall #/\ GiveBall #< HasBall ).
has_not_given_up_ball(_HasBall) :-
\+ time(_GiveBall, give_ball).
(Again, I think you should just use HasBall #=< GiveBall instead of the negated constraint.)
And you can then adjust your definition like this:
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
has_not_given_up_ball(HasBall).
If a time(2, give_ball) fact is present, this behaves as before, but with an extra choice point. If I comment out that fact, it models correctly that the ball has not been given up, so one holds it for longer:
?- time(T, has_ball).
T in 0..sup.
?- time(T, has_ball), label([T]).
ERROR: Arguments are not sufficiently instantiated
...
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
T = 3 ;
T = 4 ;
T = 5 ;
T = 6 ;
T = 7 ;
T = 8 ;
T = 9.
It's only the labeling of a time that is not constrained to a finite domain that errors, as it should.
Related
I have an add2 predicate which resolves like this where s(0) is the successor of 0 i.e 1
?- add2(s(0)+s(s(0)), s(s(0)), Z).
Z = s(s(s(s(s(0)))))
?- add2(0, s(0)+s(s(0)), Z).
Z = s(s(s(0)))
?- add2(s(s(0)), s(0)+s(s(0)), Z).
Z = s(s(s(s(s(0)))))
etc..
I'm trying to do add in a predecessor predicate which will work like so
?- add2(p(s(0)), s(s(0)), Z).
Z = s(s(0))
?- add2(0, s(p(0)), Z).
Z = 0
?- add2(p(0)+s(s(0)),s(s(0)),Z).
Z = s(s(s(0)))
?- add2(p(0), p(0)+s(p(0)), Z).
Z = p(p(0))
I can't seem to find a way to do this. My code is below.
numeral(0).
numeral(s(X)) :- numeral(X).
numeral(X+Y) :- numeral(X), numeral(Y).
numeral(p(X)) :- numeral(X).
add(0,X,X).
add(s(X),Y,s(Z)) :- add(X,Y,Z).
add(p(X),Y,p(Z)) :- add(X,Y,Z).
resolve(0,0).
resolve(s(X),s(Y)) :-
resolve(X,Y).
resolve(p(X),p(Y)) :-
resolve(X,Y).
resolve(X+Y,Z) :-
resolve(X,RX),
resolve(Y,RY),
add(RX,RY,Z).
add2(A,B,C) :-
resolve(A,RA),
resolve(B,RB),
add(RA,RB,C).
In general, adding with successor arithmetic means handling successor terms, which have the shape 0 or s(X) where X is also a successor term. This is addressed completely by this part of your code:
add(0,X,X).
add(s(X),Y,s(Z)) :- add(X,Y,Z).
Now you have to make a decision; you can either handle the predecessors and the addition terms here, in add/3, or you can wrap this predicate in another one that will handle them. You appear to have chosen to wrap add/3 with add2/3. In that case, you will definitely need to create a reducing term, such as you've built here with resolve/2, and I agree with your implementation of part of it:
resolve(0,0).
resolve(s(X),s(Y)) :-
resolve(X,Y).
resolve(X+Y,Z) :-
resolve(X,RX),
resolve(Y,RY),
add(RX,RY,Z).
This is all good. What you're missing now is a way to handle p(X) terms. The right way to do this is to notice that you already have a way of deducting by one, by using add/3 with s(0):
resolve(p(X), R) :-
resolve(X, X1),
add(s(0), R, X1).
In other words, instead of computing X using X = Y - 1, we are computing X using X + 1 = Y.
Provided your inputs are never negative, your add2/3 predicate will now work.
I am baffled by the following results. I am using SWI-Prolog.
?- bagof(Q, (Q=A, (A=[a,_] ; A=[_,b])), X).
A = [_G16898, b],
X = [[_G16898, b]] ;
A = [a, _G16892],
X = [[a, _G16892]].
Notice that [a,_] and [_,b] are not unified to produce an answer A = [a,b], X=[[a,b],[a,b]].
Now, lets try the same with arithmetic constraints:
?- bagof(Q, (Q=A, (A in 1..5 ; A in 3..8)), X).
X = [A, A],
A in 3..5.
Strangely, this time the arithmetic constraints are taken together but there are no answers A in 1..5, X=[A] and A in 3..8, X=[A].
Now lets try this in yet another way:
?- bagof(Q, (Q=A, ((1 #=< A, A #=< 5) ; (3 #=< A, A #=< 8))), X).
X = [A],
A in 3..5 ;
X = [A],
A in 3..5.
The arithmetic constraints are combined like before, but we have two answers instead of one.
How can all this be explained?
EDIT: Some more strange results. Compare this:
?- A=[_,_], bagof(Q, K1^K2^(Q=A, (A=[a,K1] ; A=[K2,b])), X).
A = [_G16886, b],
X = [[_G16886, b]] ;
A = [a, _G16889],
X = [[a, _G16889]].
with this:
?- A=[a,b], bagof(Q, K1^K2^(Q=A, (A=[a,K1] ; A=[K2,b])), X).
A = [a, b],
X = [[a, b], [a, b]].
Thats an artefact of SWI-Prolog, which also does copy attributed variables while taking findall/3 copies. findall/3 copies are used inside bagof/3 before doing keysort/2. But the effect can already be explained by means of findall/3:
SWI-Prolog, findall/3 copies attributed variables conditions:
?- findall(A, A in 1..5, L).
L = [_3464],
_3464 in 1..5.
?- findall(A, (A in 1..5, (true; true)), L).
L = [_3762, _3768],
_3762 in 1..5,
_3768 in 1..5
Jekejeke Prolog, findall/3 does not copy attributed variables conditions:
?- findall(A, A in 1..5, L).
L = [_A]
?- findall(A, (A in 1..5, (true; true)), L).
L = [_A, _B]
Inside bagof/3, there is not only a keysort/2 step, but also step where variables are restored. In this step for SWI-Prolog, constraints might be joined, since constraints will be present.
This explains the first result in the question of the OP. The second result in the question of the OP can be explained in that SWI-Prolog does goal expansion and introduce new variables in the case of (#=<)/2. You can check yourself:
?- [user].
test(A) :- A in 1..5 ; A in 3..8.
test(A) :- (1 #=< A, A #=< 5) ; (3 #=< A, A #=< 8).
?- listing(test/1).
test(A) :-
( ( integer(A)
-> between(1, 5, A)
; clpfd:clpfd_in(A, 1..5)
)
; integer(A)
-> between(3, 8, A)
; clpfd:clpfd_in(A, 3..8)
).
test(A) :-
( ( integer(A)
-> A>=1
; B=1,
clpfd:clpfd_geq(A, B)
),
( integer(A)
-> 5>=A
; C=5,
clpfd:clpfd_geq(C, A)
)
; ( integer(A)
-> A>=3
; D=3,
clpfd:clpfd_geq(A, D)
),
( integer(A)
-> 8>=A
; E=8,
clpfd:clpfd_geq(E, A)
)
).
There are no fresh variables in the expansion of (in)/2. But I guess the fresh variables inside the (#=<)/2 expansion, then causes bagof/3 to see two solutions, instead of only one.
Edit 19.08.2019:
Now I wonder how tabling with CLP(FD) solves the problem...
counter([],[]).
counter([H|T],[[H,C1]|R]) :- counter(T,[[H,C]|R]),!, C1 is C+1.
counter([H|T],[[H,1]|R]) :- counter(T,R).
What is the effect of the "!" as I'm getting the same output for an input in both the above and below code?
counter([],[]).
counter([H|T],[[H,C1]|R]) :- counter(T,[[H,C]|R]),C1 is C+1.
counter([H|T],[[H,1]|R]) :- counter(T,R).
I'm new to Prolog.
What is the effect of the "!"
The cut prunes the search space. That is, in an otherwise pure and monotonic program, the cut will remove some solutions or answers. As long as those are redundant that's fine. It sounds so innocent and useful, doesn't it? Let's have a look!
And lest I forget, using [E,Nr] to denote pairs is rather unusual, better use a pair E-Nr.
We will now compare counter_cut/2 and counter_sans/2.
| ?- counter_cut([a,a],Xs).
Xs = [[a,2]].
| ?- counter_sans([a,a],Xs).
Xs = [[a, 2]]
; Xs = [[a, 1], [a, 1]]. % <<< surprise !!!
So the cut-version has fewer solutions. Seems the solution counter_cut/2 retained is the right one. In this very particular case. Will it always take the right one? I will try a minimally more general query:
| ?- counter_cut([a,B],Xs).
B = a,
Xs = [[a, 2]].
| ?- counter_sans([a,B],Xs).
B = a,
Xs = [[a, 2]]
; Xs = [[a, 1], [B, 1]].
Again, _sans is chattier, and this time, it is even a bit right-er; for the last answer includes B = b. In other words,
| ?- counter_cut([a,B], Xs), B = b.
fails. % incomplete !
| ?- counter_sans([a,B], Xs), B = b.
B = b,
Xs = [[a,1],[b,1]].
So sometimes the _cut version is better, and sometimes _sans. Or to put more directly: Both are wrong somehow, but the _sans-version at least includes all solutions.
Here is a "purified" version, that simply rewrites the last rule into two different cases: One for the end of the list and the other for a further, different element.
counter_pure([],[]).
counter_pure([H|T],[[H,C1]|R]) :- counter_pure(T,[[H,C]|R]), C1 is C+1.
counter_pure([H],[[H,1]]).
counter_pure([H,D|T],[[H,1]|R]) :- dif(H,D), counter_pure([D|T],R).
From an efficiency viewpoint that is not too famous.
Here is a test case for efficiency for a system with rational tree unification:
?- Es = [e|Es], counter(Es, Dict).
resource_error(stack).
Instead, the implementation should loop smoothly, at least till the end of this universe. Strictly speaking, that query has to produce a resource error, but only after it has counted up to a number much larger than 10^100000000.
Here's my pure and hopefully efficient solution:
counter([X|L], C):- counter(L, X, 1, C).
counter([],X, Cnt, [[X,Cnt]]).
counter([Y|L], X, Cnt, [[X,Cnt]|C]):-
dif(X, Y),
counter(L, Y, 1, C).
counter([X|L],X, Cnt, [[X,XCnt]|C]):-
Cnt1 #= Cnt+1,
Cnt1 #=< XCnt,
counter(L, X, Cnt1, [[X,XCnt]|C]).
Using if_3 as suggested by #false:
counter([X|L], C):- counter(L, X, 1, C).
counter([],X, Cnt, [[X,Cnt]]).
counter([Y|L], X, Cnt, [[X,XCnt]|C]):-
if_(X=Y,
(
Cnt1 #= Cnt+1,
Cnt1 #=< XCnt,
counter(L, X, Cnt1, [[X,XCnt]|C])
),
(
XCnt=Cnt,
counter(L, Y, 1, C)
)
).
The cut operator ! commits to the current derivation path by pruning all choice points. Given some facts
fact(a).
fact(b).
you can compare the answers with and without cut:
?- fact(X).
X = a ;
X = b.
?- fact(X), !.
X = a.
As you can see, the general query now only reports its first success. Still, the query
?- fact(b), !.
true.
succeeds. This means, that cut violates the interpretation of , as logical conjunction:
?- X = b, fact(X), !.
X = b.
?- fact(X), !, X=b.
false.
but from our understanding of conjunction, A ∧ B should hold exactly when B ∧ A holds. So why do this at all?
Efficiency: cuts can be used such that they only change execution properties but not the answers of a predicate. These so called green cuts are for instance described in Richard O'Keefe's Craft of Prolog. As demonstrated above, maintaining correctness of a predicate with cut is much harder than one without, but obviously, correctness should come before efficiency.
It looks as if your problem was green, but I am not 100% sure if there is not a change in the answers.
Negation: logical negation according to the closed world assumption is expressed with cut. You can define neg(X) as:
neg(X) :-
call(X),
!,
false.
neg(_) :-
true.
So if call(X) succeeds, we cut the choice point for the second rule away and derive false. Otherwise, nothing is cut and we derive true. Please be aware that this is not negation in classical logic and that it suffers from the non-logical effects of cut. Suppose you define the predicate land/1 to be one of the continents:
land(africa).
land(america).
land(antarctica).
land(asia).
land(australia).
land(europe).
and then define water as everything not on land:
water(X) :-
neg(land(X)).
then you can correctly obtain:
?- water(pacific).
true.
?- water(africa).
false.
But you can also derive:
?- water(space).
true.
which should not hold. In particular, in classical logic:
land(africa) ∧
land(america) ∧
land(antarctica) ∧
land(asia) ∧
land(australia) ∧
land(europe) → ¬ land(space).
is not valid. Again, you should know well what you are doing if you use negation in Prolog.
Here is my attempt using if_/3:
counter([], []).
counter([H|T], [[H,C]|OutT] ):-
if_(
T=[],
(C = 1,OutT=[]),
(
[H|T] = [H,H1|T2],
if_(
H=H1,
(counter([H1|T2], [[H1,C1]|OutT]), C is C1+1),
(C = 1, counter([H1|T2], OutT))
)
)
).
I have two, slightly different, implementations of a predicate, unique_element/2, in Prolog. The predicate succeeds when given an element X and a list L, the element X appears only once in the list. Below are the implementations and the results:
Implementation 1:
%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
not(member(Elem, T)).
unique_element(Elem, [H|T]) :-
member(Elem, T),
H\==Elem,
unique_element(Elem, T),
!.
Results:
?- unique_element(X, [a, a, b, c, c, b]).
false.
?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.
Implementation 2:
%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
not(member(Elem, T)).
unique_element(Elem, [H|T]) :-
H\==Elem,
member(Elem, T),
unique_element(Elem, T),
!.
In case you didn't notice at first sight: H\==Elem and member(Elem, T) are flipped on the 2nd impl, rule 2.
Results:
?- unique_element(X, [a, a, b, c, c, b]).
X = a.
?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.
Question: How does the order, in this case, affect the result? I realize that the order of the rules/facts/etc matters. The two specific rules that are flipped though, don't seem to be "connected" or affect each other somehow (e.g. a cut in the wrong place/order).
Note: We are talking about SWI-Prolog here.
Note 2: I am aware of, probably different and better implementations. My question here is about the order of sub-goals being changed.
H\==Elem is testing for syntactic inequality at the point in time when the goal is executed. But later unification might make variables identical:
?- H\==Elem, H = Elem.
H = Elem.
?- H\==Elem, H = Elem, H\==Elem.
false.
So here we test if they are (syntactically) different, and then they are unified nevertheless and thus are no longer different. It is thus just a temporary test.
The goal member(Elem, T) on the other hand is true if that Elem is actually an element of T. Consider:
?- member(Elem, [X]).
Elem = X.
Which can be read as
(When) does it hold that Elem is an element of the list [X]?
and the answer is
It holds under certain circumstances, namely when Elem = X.
If you now mix those different kinds of goals in your programs you get odd results that can only explained by inspecting your program in detail.
As a beginner, it is best to stick to the pure parts of Prolog only. In your case:
use dif/2 in place of \==
do not use cuts - in your case it limits the number of answers to two. As in
unique_element(X, [a,b,c])
do not use not/1 nor (\+)/1. It produces even more incorrectness. Consider unique_element(a,[a,X]),X=b. which incorrectly fails while X=b,unique_element(a,[a,X]) correctly succeeds.
Here is a directly purified version of your program. There is still room for improvement!
non_member(_X, []).
non_member(X, [E|Es]) :-
dif(X, E),
non_member(X, Es).
unique_element(Elem, [Elem|T]) :-
non_member(Elem, T).
unique_element(Elem, [H|T]) :-
dif(H,Elem),
% member(Elem, T), % makes unique_element(a,[b,a,a|Xs]) loop
unique_element(Elem, T).
?- unique_element(a,[a,X]).
dif(X, a)
; false. % superfluous
?- unique_element(X,[E1,E2,E3]).
X = E1, dif(E1, E3), dif(E1, E2)
; X = E2, dif(E2, E3), dif(E1, E2)
; X = E3, dif(E2, E3), dif(E1, E3)
; false.
Note how the last query reads?
When is X a unique element of (any) list [E1,E2,E3]?
The answer is threefold. Considering one element after the other:
X is E1 but only if it is different to E2 and E3
etc.
TL;DR: Read the documentation and figure out why:
?- X = a, X \== a.
false.
?- X \== a, X = a.
X = a.
I wonder why you stop so close from figuring it out yourself ;-)
There are too many ways to compare things in Prolog. At the very least, you have unification, which sometimes can compare, and sometimes does more; than you have equvalence, and its negation, the one you are using. So what does it do:
?- a \== b. % two different ground terms
true.
?- a \== a. % the same ground term
false.
Now it gets interesting:
?- X \== a. % a free variable and a ground term
true.
?- X \== X. % the same free variable
false.
?- X \== Y. % two different free variables
true.
I would suggest that you do the following: figure out how member/2 does its thing (does it use unification? equivalence? something else?) then replace whatever member/2 is using in all the examples above and see if the results are any different.
And since you are trying to make sure that things are different, try out what dif/2 does. As in:
?- dif(a, b).
or
?- dif(X, X).
or
?- dif(X, a).
and so on.
See also this question and answers: I think the answers are relevant to your question.
Hope that helps.
Here is another possibility do define unique_element/2 using if_/3 and maplist/2:
:- use_module(library(apply)).
unique_element(Y,[X|Xs]) :-
if_(Y=X,maplist(dif(Y),Xs),unique_element(Y,Xs)).
In contrast to #user27815's very elegant solution (+s(0)) this version does not build on clpfd (used by tcount/3). The example queries given by the OP work as expected:
?- unique_element(a,[a, a, b, c, c, b]).
no
?- unique_element(X,[a, b, c, c, b, d]).
X = a ? ;
X = d ? ;
no
The example provided by #false now succeeds without leaving a superfluous choicepoint:
?- unique_element(a,[a,X]).
dif(a,X)
The other more general query yields the same results:
?- unique_element(X,[E1,E2,E3]).
E1 = X,
dif(X,E3),
dif(X,E2) ? ;
E2 = X,
dif(X,E3),
dif(X,E1) ? ;
E3 = X,
dif(X,E2),
dif(X,E1) ? ;
no
Can you not define unique_element like tcount Prolog - count repetitions in list
unique_element(X, List):- tcount(=(X),List,1).
I'm trying to define an operator =>> that checks if one of its operands is double of the other operand.
I tried so far:
:- op(200, xfy, =>>).
=>>(L, R) :- double(L, R); double(R, L).
double(L, R) :- L is R * 2.
But when used in RPEL, I got :
?- (-8) =>> (-4).
true ;
false.
%^^^^ note here
?- 7 =>> 3.
false.
?- 40 =>> 20.
true ;
false.
%^^^^ note here
?- 20 =>> 40.
true.
What is the problem? How can I fix it?
There are several issues. First, defining an operator for such a tiny task is a bit of an overkill. Always keep in mind the cost of declaring an operator: Every time you define an operator you change the language a bit which means that people who read that program text will have to learn that syntax as well.
So best would be to just stay with a simple predicate name. And if you really insist on it, try to use operators in a way, similar to existing operators. We have roughly the following three groups in ISO Prolog according to their priority:
1200-900: Rules, control constructs. Most notably conjunction is 1000.
700, xfx: Comparison related infix operators like: = \= == \== #< #=< #> #>= =.. is =:= =\= < =< > >=. Note that these are all non-associative, since nesting is not specific to their meaning.
500-200: Expressions.
Also note that all the symmetric relations have symmetric names — except for the negated ones: \= and \==.
:- op(700,xfx,=:*:=).
=:*:=(X, Y) :-
(X - 2*Y) * (Y - 2*X) =:= 0.
The following might be preferable since the intermediary results are smaller and thus multiplication is cheaper and never produces an overflow:
=:*:=(X, Y) :-
sign(X - 2*Y) * sign(Y - 2*X) =:= 0.
This is a determinism issue: There may be further solutions ((;)/2 can be read as "or"), and therefore Prolog backtracks (and finds no alternative).
There is an easy way to fix this: Use once/1 to commit to the first solution, if any:
L =>> R :- once((double(L, R) ; double(R, L))).
Notice also that you may want to use =:=/2, not is/2, in this case. Even better, if you are working over integers, simply use CLP(FD) constraints, and your predicate will be deterministic and much more general:
:- use_module(library(clpfd)).
L =>> R :- L #= R*2 #\/ R #= L*2.
Examples:
?- 40 =>> 20.
true.
?- 40 =>> X, X #< 80.
X = 20.
?- X =>> Y, X #= 2, Y #= 3.
false.