I’m using: SWI-Prolog version 8.0.3 for x86_64-darwin
I’m very grateful, in advance, for any explanation of this behavior from experts. I’ve been playing with abduction lately, trying to apply it to logical operators. My code includes the following predicates, based on the discussion of abduction in Peter Flach’s book Simply Logical:
abduce(P, E) :- abduce(P, [], E, [], _Seen).
% ...
% I'm omitting most the of clauses of abduce/5, as this seems to be the one that's malfunctioning
abduce(P, E, [P | E], S, [P | S]) :-
abducible(P), !,
\+ member(\+P, E).
% ...
% there's also is a similar set of clauses for abducing negations, including the following:
abduce_false(P, E, [\+P | E], S0, [\+P | S0]) :-
abducible(P), !,
\+ member(P, E).
% ...
abducible(ostrich(_)).
and(A, B) :- A, B.
It seems like Prolog is simply ignoring my calls to member/2 in the above code sometimes. When I fire up the interpreter, I can get this response to a query:
?- abduce(and(ostrich(X), \+ostrich(X)), H)
H = [\+ostrich(X), ostrich(X)].
This shouldn’t be happening, since the only way to add \+ostrich(X) to the explanation list E is if E doesn’t contain ostrich(X), which it does. When I run the trace, the same query fails! And after I run the trace, I consistently get
?- abduce(and(ostrich(X), \+ostrich(X)), H)
false.
Also, after doing the trace, abduce/2 behaves correctly with other arguments that would have caused it to misbehave in a similar manner previously. For illustration I’ve copy/pasted (minus most of the trace) my interpreter session below. I can also give more of the omitted details if need be. I’m pretty confident the problem is in the clause quoted above, especially since the predicate begins to behave as expected after a trace.
?- abduce(and(ostrich(X), \+ostrich(X)), H).
H = [\+ostrich(X), ostrich(X)].
?- debug.
Correct to: "edinburgh:debug"? yes
true.
[debug] ?- abduce(and(ostrich(X), \+ostrich(X)), H).
H = [\+ostrich(X), ostrich(X)].
[debug] ?- trace.
true.
[trace] ?- abduce(and(ostrich(X), \+ostrich(X)), H).
Call: (8) abduce(and(ostrich(_7792), \+ostrich(_7792)), _7812) ? creep
Call: (9) abduce(and(ostrich(_7792), \+ostrich(_7792)), [], _7812, [], _8098) ? creep
...
Fail: (8) abduce(and(ostrich(_7792), \+ostrich(_7792)), _7812) ? creep
false.
[trace] ?- notrace.
true.
[debug] ?- abduce(and(ostrich(X), \+ostrich(X)), H).
false.
UPDATE: on a whim I tried explicitly replacing member/2 with what I thought would function the same:
my_member(X, [X|_]).
my_member(X, [_|T]) :- my_member(X, T).
The code now gives the correct answer even before running the trace. So I guess the issue is with the builtin member/2.