Unbelievably, there is a partial solution to this problem here. To reproduce the relevant portion here is substantial code, so let me make it clear that this is not my own work, I am just including the work here in case the website above goes missing in the future.
First, you need a meta-circular interpreter:
mi_circ(true).
mi_circ((A,B)) :-
mi_circ(A),
mi_circ(B).
mi_circ(clause(A,B)) :-
clause(A,B).
mi_circ(A \= B) :-
A \= B.
mi_circ(G) :-
G \= true,
G \= (_,_),
G \= (_\=_),
G \= clause(_,_),
clause(G, Body),
mi_circ(Body).
This works for \=/2
and clause/2
. To generalize this pattern to all built-in predicates, we can use predicate_property/2 to identify them as such for calling them directly:
provable(true, _) :- !.
provable((G1,G2), Defs) :- !,
provable(G1, Defs),
provable(G2, Defs).
provable(BI, _) :-
predicate_property(BI, built_in),
!,
call(BI).
provable(Goal, Defs) :-
member(Def, Defs),
copy_term(Def, Goal-Body),
provable(Body, Defs).
This gives you a reified meta-interpreter, meaning you can pass provable/2
a goal and a set of definitions and it will tell you whether the definitions supplied are sufficient to prove the goal. I bet you can taste how close we are to the final solution now!
With the following additional definitions, we can use this MI to identify redundant facts in some predicate definitions:
redundant(Functor/Arity, Reds) :-
functor(Term, Functor, Arity),
findall(Term-Body, clause(Term, Body), Defs),
setof(Red, Defs^redundant_(Defs, Red), Reds).
redundant_(Defs, Fact) :-
select(Fact-true, Defs, Rest),
once(provable(Fact, Rest)).
This is using select/3
to portion out one definition at a time and see if the predicate is still provable. By doing that across all the definitions you can get the set of all unnecessary rules.
Given the definitions:
as([]).
as([a]). % redundant
as([a,a]). % redundant
as([A|As]) :-
A = a, % test built-in =/2
5 is 2 + 3, % test built-in is/2
1 > 0, % test built-in >/2
as(As).
we can ask for facts which are deducible from all (respective) remaining clauses and hence redundant:
?- redundant(as/1, Reds).
Reds = [as([a]), as([a, a])]
Alas, this does not work out-of-the-box on your problem, but I do think that with some study you could find a way to apply this technique to it and come up with something. For instance, you could create a meta-interpreter that takes a list of facts to check and perform the same sort of remove-one-then-prove loop to find them.
Hope this helps and is at least interesting.