This is my code, which is for Satchmo theorem proving. It does some unification.
:- op(700, xfx, ==>).
:- op(400, yfx, &).
:- op(400, yfx, or).
fact([a, 9]).
fact([b, 9]).
rule([a, X] & [b, X] ==> [c, X]). %% horn bit
rule([c, X] ==> [r, X] or [s, X]). %% non horn bit
rule([r, X] ==> [t, X]).
rule([s, X] ==> [t, X]).
horn(A & B) :-
!,
horn(A),
horn(B).
horn(A or B) :-
!,
(horn(A); horn(B)).
horn(P) :-
fact(P).
horn(P) :-
temp(P).
horn(P) :-
rule(SUBGOALS ==> P),
\+ P = (_A or _B),
horn(SUBGOALS).
satchmo(P) :-
retractall(temp(_)),
prove(P).
prove(P) :-
horn(P).
prove(P) :-
rule(LHS ==> (A or B)),
horn(LHS),
\+ horn(A or B),
cprove(A ==> P),
cprove(B ==> P).
cprove(A ==> P) :-
try(A),
(prove(P) ->
untry(A);
(untry(A), fail)).
try(A & B) :-
!,
try(A),
try(B).
try(A) :-
assert(temp(A)).
untry(A & B) :-
!,
untry(A),
untry(B).
untry(A) :-
retract(temp(A)).
To understand how is it working, we can do that by ?- spy([satchmo]).
1- if the given query is fact as:
?- satchmo([a, 9]).
yes.
or
?- satchmo([b, 9]).
yes.
the program will prove it as it is a fact.
2- If the query in the horn bit as:
?- satchmo([c, 9]).
yes.
the program will prove it as it is the horn rule.
3- If the query in the non-horn bit as:
?- satchmo([t, 9]).
yes.
It'll be proved as well.
This is working perfectly. But when I have tried to change it a bit. Instead of unification I need to do another kind of matching which can prove the following:
if I have:
rule[living, X] ==> [mortal, X].
[man, socrates].
I would like to prove:
?- satchmo([mortal, socrates]).
yes.
To do this I have amend my code a bit, so instead of having:
horn(P):-
fact(P).
I put something very similar:
horn(P):-
match(P, P0),
fact(P0).
and I have defined match as:
match(X, Y):-
X=Y.
I know that I have not do anything by this move, but I am thinking to amend the definition of match a bit to be able to prove what I need.
I am stuck somewhere here, look at my current code.
:- op(700, xfx, ==>).
:- op(400, yfx, &).
:- op(400, yfx, or).
:- op(400, yfx, <<<).
fact([a, 9]).
fact([b, 9]).
rule([a, X] & [b, X] ==> [c, X]). %% horn bit
rule([c, X] ==> [r, X] or [s, X]). %% non horn bit
rule([r, X] ==> [t, X]).
rule([s, X] ==> [t, X]).
man <<< human.
human <<< animal.
animal <<< living.
subset(X, X).
subset(X, Y) :-
X <<< Y.
subset(X, Z) :-
X <<< Y,
subset(Y, Z).
horn(A & B) :-
!,
horn(A),
horn(B).
horn(A or B) :-
!,
(horn(A); horn(B)).
horn(P) :-
fact(P).
horn(P) :-
temp(P).
horn(P) :-
rule(SUBGOALS ==> P),
\+ P = (_A or _B),
horn(SUBGOALS).
satchmo(P) :-
retractall(temp(_)),
prove(P).
prove(P) :-
horn(P).
prove(P) :-
rule(LHS ==> (A or B)),
horn(LHS),
\+ horn(A or B),
cprove(A ==> P),
cprove(B ==> P).
cprove(A ==> P) :-
try(A),
(prove(P) ->
untry(A);
(untry(A), fail)).
try(A & B) :-
!,
try(A),
try(B).
try(A) :-
assert(temp(A)).
untry(A & B) :-
!,
untry(A),
untry(B).
untry(A) :-
retract(temp(A)).
Here, we can test the subset:
?- subset(human, man).
yes.
The problem here, is I do not know how to achieve my goal by proving:
?- satchmo([mortal, socrates]).
yes.
from:
[living, X] ==> [mortal, X].
[man, socrates].
Could it be done by changing the definition of match? If not, is there any other method to do it?
Ok, done,
The solution of this problem is simply by changing the definition of match, so it will be as follows:
match(P, P0) :-
P = [X, _],
P0 = [Y, _],
subset(Y, X).
subset(X, X).
subset(X, Y) :-
X <<< Y.
subset(X, Z) :-
X <<< Y,
subset(Y, Z).
and when I have this facts:
fact([man, socrates]).
man <<< human.
human <<< animal.
animal <<< living.
and this rule:
rule([living, X] ==> [mortal, X]).
I can prove:
?- satchmo([mortal, socrates]).
Related
I tried this vanilla interpreter:
solve(true) :- !, true.
solve(X is E) :- !, X is E.
solve((A,B)) :- !, solve(A), solve(B).
solve(H) :- clause(H,B), solve(B).
Can we use it to meta-interpret some code? I tried this code,
requires SWI-Prolog 8.3.19, which runs fine normally:
sumlist([X|Y], R) => sumlist(Y, H), R is X+H.
sumlist([], R) => R is 0.
?- sumlist([1,2,3],X).
X = 6.
?- sumlist(X,Y).
ERROR: No rule matches sumlist(_21604,_21606)
But meta-interpretation goes wrong. The reason is that clause/2
doesn’t know about rules that use single sided unification:
?- clause(sumlist(A,B),C).
A = [_22728|_22730],
C = (sumlist(_22730, _22736), B is _22728+_22736) ;
A = [],
C = (B is 0).
?- solve(sumlist([1,2,3],X)).
X = 6.
?- solve(sumlist(X,Y)).
SWI-Prolog wurde unerwartet beendet.
Is there a solution for meta-interpreters and single sided unification?
One way out of the dilemma and stay inside the ISO core standard, is to translate single sided unfication to a combination of nonvar/1, (=)/2 and (==)/2, like here:
?- clause(sumlist(X,Y),Z), write((sumlist(X,Y):-Z)), nl, fail; true.
sumlist(_A, _B) :- nonvar(_A), _A = [_C|_D], sumlist(_D, _E), _B is _C+_E
sumlist(_A, _B) :- nonvar(_A), _A = [], _B is 0
Of course we need to add the built-ins nonvar/1, (=)/2 and (==)/2 as well to the meta interpreter:
solve(true) :- !.
solve(X is E) :- !, X is E.
solve(nonvar(X)) :- !, nonvar(X).
solve(X == Y) :- !, X == Y.
solve(X = Y) :- !, X = Y.
solve((A, B)) :- !, solve(A), solve(B).
solve(H) :- clause(H, B), solve(B).
Meta-interpreting sumlist/2 now works fine:
?- solve(sumlist([1,2,3],X)).
X = 6
?- solve(sumlist(X,Y)).
No
But the translator might challenge a Prolog system concering clause indexing. It moves away the functors from the head into the body. So the Prolog system would need some body front indexing as pioneered by YAP and found in Jekejeke Prolog.
Open Source:
Yet Another Pattern Matcher
https://gist.github.com/jburse/a3517410a28b759ef44f72584f89aaf8#file-picat3-pl
Vanilla Interpreter, Expansion Solution
https://gist.github.com/jburse/a3517410a28b759ef44f72584f89aaf8#file-vanilla4-pl
It turns out that SICStus Prolog doesn't have an occurs_check
Prolog flag. At least we couldn't find one, and this here
gives an error message:
/* SICStus 4.6.0 (x86_64-win32-nt-4) */
?- set_prolog_flag(occurs_check, true).
Domain error in argument 1 of set_prolog_flag/2
It seems the value "true" is not so much a problem, the
crictical unifications can be realized via the existing
built-in unify_with_occurs_check/2. An interesting value
of an occurs_check Prolog flag is the value "error".
How would one implement a predicate unify_with_occurs_check_and_error/2 ?
Please note, the solution for unify_with_occurs_check_and_error/2
should behave like unify_with_occurs_check/2, i.e. not trigger
attributed variables.
Here is an example usage of the Prolog flag where present:
?- set_prolog_flag(occurs_check, error).
true.
?- X = f(X).
ERROR: ...
And this is what one would do in SICStus Prolog:
?- unify_with_occurs_check_and_error(X, f(X)).
ERROR: ...
Was adapting the code from here and got the following solution:
unify_with_error(X, Y) :- var(X), var(Y), !, X = Y.
unify_with_error(X, Y) :- var(X), !, must_notin(X, Y), X = Y.
unify_with_error(X, Y) :- var(Y), !, must_notin(Y, X), X = Y.
unify_with_error(X, Y) :- functor(X, F, A), functor(Y, G, B),
F/A = G/B,
X =.. [_|L],
Y =.. [_|R],
maplist(unify_with_error, L, R).
must_notin(X, Y) :-
term_variables(Y, L),
maplist(\==(X), L), !.
must_notin(X, Y) :-
throw(error(occurs_check(X, Y),_)).
Seems to work and no interference with attributed variables:
/* SICStus 4.6.0 (x86_64-win32-nt-4) */
?- unify_with_error(X, f(X)).
error(occurs_check(_413,f(_413)),_409)
?- freeze(X, throw(ball)), unify_with_error(X, f(X)).
error(occurs_check(_413,f(_413)),_409)
I wonder whether there is a pure Prolog meta-interpreter with
only one rule. The usual Prolog vanilla meta-interpreter has two
rules. It reads as follows:
solve(true).
solve((A, B)) :- solve(A), solve(B). /* rule 1 */
solve(H) :- program(H, B), solve(B). /* rule 2 */
This Prolog vanilla meta-interpreter uses two rules /* rule 1 */
and /* rule 2 */. And the rest is facts. The program that
is executed is represented by program facts. Here is an example program:
program(append([], X, X), true).
program(append([X|Y], Z, [X|T]), append(Y, Z, T)).
program(nrev([], []), true).
program(nrev([H|T], R), (nrev(T, S), append(S, [H], R))).
And an example query:
?- solve(nrev([1,2,3], X)).
X = [3, 2, 1] .
Is there a way to represent the program differently as facts, and
then code a different meta-interpreter, which would use only facts
except for a single rule instead of two rules? Something that would
work for all pure Prolog programs, not only the nrev example?
Here is one idea, using a list to hold the rest of the computation:
solve([]).
solve([X|Xs]) :- program(X, Ys, Xs), solve(Ys).
program(true, Xs, Xs).
program(append([],X,X), Xs, Xs).
program(append([X|Y], Z, [X|T]), [append(Y,Z,T)|Xs], Xs).
program(nrev([],[]), Xs, Xs).
program(nrev([H|T],R), [nrev(T,S),append(S,[H],R)|Xs], Xs).
With test call (where one needs to wrap the call in a list).
?- solve([nrev([1,2,3],X)]).
X = [3,2,1] ? ;
no
Arguably, one could represent the program/3 facts as a DCG instead, for increased readability (but then it might not be considered a "fact" any more).
Here is another approach, known as binarization with continuation.
Its from this logic transformers paper here by Paul Tarau (2021).
solve(true).
solve(X) :- program(X, Y), solve(Y).
program(append([],X,X,C), C).
program(append([X|Y],Z,[X|T],C), append(Y,Z,T,C)).
program(nrev([],[],C), C).
program(nrev([H|T],R,C), nrev(T,S,append(S,[H],R,C))).
A little sanity check shows that it wurks:
?- solve(nrev([1,2,3], X, true)).
X = [3, 2, 1] ;
No
If ;/2 is allowed, then this seems to work:
solve(true).
solve(H) :- ((X, Y) = H, solve(X), solve(Y)); (program(H :- B), solve(B)).
program(append([], X, X) :- true).
program(append([X|Y], Z, [X|T]) :- append(Y, Z, T)).
program(nrev([], []) :- true).
program(nrev([H|T], R) :- (nrev(T, S), append(S, [H], R))).
Test:
?- solve(nrev([1,2,3], X)).
X = [3, 2, 1] ;
false.
I am struggling to identify a way to convert a list to string. This list is the output from a findall predicate.
see below my code.
edge(a,b).
edge(a,c).
edge(b,c).
edge(c,d).
edge(c,e).
edge(d,e).
edge(f,g).
edge(g,h).
route(X, Z, []) :- edge(X, Z).
route(X, Z, [Y|T]) :- edge(X, Y), route(Y, Z, T).
allways(X, Y) :- findall(E, (route(X, Y, E), write(E), nl), _).
%allways(X, Y) :- findall(E, (route(X, Y, E), with_output_to(atom(_),maplist(write, E)), nl), _).
%allways(X, Y) :- findall(E, (route(X, Y, E), atomic_list_concat(E,'',A), nl), _),write(A).
my output
?- allways(a,e).
[b,c]
[b,c,d]
[c]
[c,d]
expected output
?- allways(a,e).
bc
bcd
c
cd
I tried different ways to convert the list to string using with_output_to predicate and atomic_list_concat predicate but nothing works.
When you say "string", what do you mean?
There are many predicates for concatenating a list to an atom. In your case atomic_list_concat/2 sounds about right.
Your problem however is in how you wrote the findall, you are capturing the wrong variable.
?- findall(Str, ( route(a, e, E), atomic_list_concat(E, Str) ), Strs),
forall(member(Str, Strs), format("~w~n", [Str])).
or, if you do not need the list at all, do not use findall at all.
?- forall(( route(a, e, E),
atomic_list_concat(E, Str)
),
format("~w~n", [Str])).
I'm writing a prolog program that will check if two math expressions are actually the same. For example, if my math expression goal is: (a + b) + c then any of the following expressions are considered the same:
(a+b)+c
a+(b+c)
(b+a)+c
(c+a)+b
a+(c+b)
c+(a+b)
and other combinations
Certainly, I don't expect to check the combination of possible answers because the expression can be more complex than that.
Currently, this is my approach:
For example, if I want to check if a + b *c is the same with another expression such as c*b+a, then I store both expression recursively as binary expressions, and I should create a rule such as ValueOf that will give me the "value" of the first expression and the second expression. Then I just check if the "value" of both expression are the same, then I can say that both expression are the same. Problem is, because the content of the expression is not number, but identifier, I cannot use the prolog "is" keyword to get the value.
Any suggestion?
many thanks
% represent a + b * c
binExprID(binEx1).
hasLeftArg(binEx1, a).
hasRightArg(binEx1, binEx2).
hasOperator(binEx1, +).
binExprID(binEx2).
hasLeftArg(binEx2, b).
hasRightArg(binEx2, c).
hasOperator(binEx2, *).
% represent c * b + a
binExprID(binEx3).
hasLeftArg(binEx3, c).
hasRightArg(binEx3, b).
hasOperator(binEx3, *).
binExprID(binEx4).
hasLeftArg(binEx4, binEx3).
hasRightArg(binEx4, a).
hasOperator(binEx4, +).
goal:- valueOf(binEx1, V),
valueOf(binEx4, V).
Math expressions can be very complex, I presume you are referring to arithmetic instead. The normal form (I hope my wording is appropriate) is 'sum of monomials'.
Anyway, it's not an easy task to solve generally, and there is an ambiguity in your request: 2 expressions can be syntactically different (i.e. their syntax tree differ) but still have the same value. Obviously this is due to operations that leave unchanged the value, like adding/subtracting 0.
From your description, I presume that you are interested in 'evaluated' identity. Then you could normalize both expressions, before comparing for equality.
To evaluate syntactical identity, I would remove all parenthesis, 'distributing' factors over addends. The expression become a list of multiplicative terms. Essentially, we get a list of list, that can be sorted without changing the 'value'.
After the expression has been flattened, all multiplicative constants must be accumulated.
a simplified example:
a+(b+c)*5 will be [[1,a],[b,5],[c,5]] while a+5*(c+b) will be [[1,a],[5,c],[5,b]]
edit after some improvement, here is a very essential normalization procedure:
:- [library(apply)].
arith_equivalence(E1, E2) :-
normalize(E1, N),
normalize(E2, N).
normalize(E, N) :-
distribute(E, D),
sortex(D, N).
distribute(A, [[1, A]]) :- atom(A).
distribute(N, [[1, N]]) :- number(N).
distribute(X * Y, L) :-
distribute(X, Xn),
distribute(Y, Yn),
% distribute over factors
findall(Mono, (member(Xm, Xn), member(Ym, Yn), append(Xm, Ym, Mono)), L).
distribute(X + Y, L) :-
distribute(X, Xn),
distribute(Y, Yn),
append(Xn, Yn, L).
sortex(L, R) :-
maplist(msort, L, T),
maplist(accum, T, A),
sumeqfac(A, Z),
exclude(zero, Z, S),
msort(S, R).
accum(T2, [Total|Symbols]) :-
include(number, T2, Numbers),
foldl(mul, Numbers, 1, Total),
exclude(number, T2, Symbols).
sumeqfac([[N|F]|Fs], S) :-
select([M|F], Fs, Rs),
X is N+M,
!, sumeqfac([[X|F]|Rs], S).
sumeqfac([F|Fs], [F|Rs]) :-
sumeqfac(Fs, Rs).
sumeqfac([], []).
zero([0|_]).
mul(X, Y, Z) :- Z is X * Y.
Some test:
?- arith_equivalence(a+(b+c), (a+c)+b).
true .
?- arith_equivalence(a+b*c+0*77, c*b+a*1).
true .
?- arith_equivalence(a+a+a, a*3).
true .
I've used some SWI-Prolog builtin, like include/3, exclude/3, foldl/5, and msort/2 to avoid losing duplicates.
These are basic list manipulation builtins, easily implemented if your system doesn't have them.
edit
foldl/4 as defined in SWI-Prolog apply.pl:
:- meta_predicate
foldl(3, +, +, -).
foldl(Goal, List, V0, V) :-
foldl_(List, Goal, V0, V).
foldl_([], _, V, V).
foldl_([H|T], Goal, V0, V) :-
call(Goal, H, V0, V1),
foldl_(T, Goal, V1, V).
handling division
Division introduces some complexity, but this should be expected. After all, it introduces a full class of numbers: rationals.
Here are the modified predicates, but I think that the code will need much more debug. So I allegate also the 'unit test' of what this micro rewrite system can solve. Also note that I didn't introduce the negation by myself. I hope you can work out any required modification.
/* File: arith_equivalence.pl
Author: Carlo,,,
Created: Oct 3 2012
Purpose: answer to http://stackoverflow.com/q/12665359/874024
How to check if two math expressions are the same?
I warned that generalizing could be a though task :) See the edit.
*/
:- module(arith_equivalence,
[arith_equivalence/2,
normalize/2,
distribute/2,
sortex/2
]).
:- [library(apply)].
arith_equivalence(E1, E2) :-
normalize(E1, N),
normalize(E2, N), !.
normalize(E, N) :-
distribute(E, D),
sortex(D, N).
distribute(A, [[1, A]]) :- atom(A).
distribute(N, [[N]]) :- number(N).
distribute(X * Y, L) :-
distribute(X, Xn),
distribute(Y, Yn),
% distribute over factors
findall(Mono, (member(Xm, Xn), member(Ym, Yn), append(Xm, Ym, Mono)), L).
distribute(X / Y, L) :-
normalize(X, Xn),
normalize(Y, Yn),
divide(Xn, Yn, L).
distribute(X + Y, L) :-
distribute(X, Xn),
distribute(Y, Yn),
append(Xn, Yn, L).
sortex(L, R) :-
maplist(dsort, L, T),
maplist(accum, T, A),
sumeqfac(A, Z),
exclude(zero, Z, S),
msort(S, R).
dsort(L, S) :- is_list(L) -> msort(L, S) ; L = S.
divide([], _, []).
divide([N|Nr], D, [R|Rs]) :-
( N = [Nn|Ns],
D = [[Dn|Ds]]
-> Q is Nn/Dn, % denominator is monomial
remove_common(Ns, Ds, Ar, Br),
( Br = []
-> R = [Q|Ar]
; R = [Q|Ar]/[1|Br]
)
; R = [N/D] % no simplification available
),
divide(Nr, D, Rs).
remove_common(As, [], As, []) :- !.
remove_common([], Bs, [], Bs).
remove_common([A|As], Bs, Ar, Br) :-
select(A, Bs, Bt),
!, remove_common(As, Bt, Ar, Br).
remove_common([A|As], Bs, [A|Ar], Br) :-
remove_common(As, Bs, Ar, Br).
accum(T, [Total|Symbols]) :-
partition(number, T, Numbers, Symbols),
foldl(mul, Numbers, 1, Total), !.
accum(T, T).
sumeqfac([[N|F]|Fs], S) :-
select([M|F], Fs, Rs),
X is N+M,
!, sumeqfac([[X|F]|Rs], S).
sumeqfac([F|Fs], [F|Rs]) :-
sumeqfac(Fs, Rs).
sumeqfac([], []).
zero([0|_]).
mul(X, Y, Z) :- Z is X * Y.
:- begin_tests(arith_equivalence).
test(1) :-
arith_equivalence(a+(b+c), (a+c)+b).
test(2) :-
arith_equivalence(a+b*c+0*77, c*b+a*1).
test(3) :-
arith_equivalence(a+a+a, a*3).
test(4) :-
arith_equivalence((1+1)/x, 2/x).
test(5) :-
arith_equivalence(1/x+1, (1+x)/x).
test(6) :-
arith_equivalence((x+a)/(x*x), 1/x + a/(x*x)).
:- end_tests(arith_equivalence).
running the unit test:
?- run_tests(arith_equivalence).
% PL-Unit: arith_equivalence ...... done
% All 6 tests passed
true.