Simplify Expressions in Prolog - prolog

I wanted to ask how I can simplify expressions like:
1+2+a*5+0/b-c*0
= 3+a*5
And especially how can I separate such expressions in lists.

It's actually kind of fun in Prolog, because you don't need to do anything too magical to make it work.
?- X-Y = 1+2+a*5+0/b-c*0.
X = 1+2+a*5+0/b,
Y = c*0.
So you could start by doing something like this:
simplify(C, C) :- atom(C) ; number(C).
simplify(X+Y, X1+Y1) :- simplify(X, X1), simplify(Y, Y1).
simplify(X*Y, X1*Y1) :- simplify(X, X1), simplify(Y, Y1).
simplify(X/Y, X1/Y1) :- simplify(X, X1), simplify(Y, Y1).
simplify(X-Y, X1-Y1) :- simplify(X, X1), simplify(Y, Y1).
This is an identity transform: it doesn't do anything.
?- simplify(1+2+a*5+0/b-c*0, Result).
Result = 1+2+a*5+0/b-c*0.
Now you can add rules for specific cases:
simplify(X*0, 0).
simplify(0*X, 0).
Now you get multiple results:
?- simplify(1+2+a*5+0/b-c*0, Result).
Result = 1+2+a*5+0/b-c*0 ;
Result = 1+2+a*5+0/b-0 ;
You could add a rule for constant folding:
simplify(X+Y, C) :- number(X), number(Y), C is X+Y.
You know, just have fun with it.
Lists aren't really any easier to work with, but you can make them using the "univ" operator: =..:
?- 1+2+a*5+0/b-c*0 =.. R.
R = [-, 1+2+a*5+0/b, c*0].

It's possible to simplify expressions in Prolog using unification, but this sometimes leads to unexpected results. In this example, two different variables in an expression are unified when "matching" a pattern, even if they were intended to be distinct:
:- initialization(main).
simplify(A+A,2*A).
main :-
simplify(A+B,C),
writeln(C).
In this case, simplify(A+B,C) would unify A with B.
To solve this problem, I use subsumes_term/2 to match a pattern without unifying the variables in an expression. subsumes_term(A+A,Input) will not match A+B unless A is already unified with B:
simplify(Input,2*A) :-
subsumes_term(A+A,Input).
This subsumes_term/2 predicate is often useful for metaprogramming: I used it to write a Prolog-to-Minizinc compiler.

Related

The unification algorithm in Prolog

I'm trying to program the unification algorithm in Prolog to verify if two expressions can unify by returning boolean True/False:
EDIT.
I found this implementation usefull:
from: http://kti.mff.cuni.cz/~bartak/prolog/data_struct.html
unify(A,B):-
atomic(A),atomic(B),A=B.
unify(A,B):-
var(A),A=B. % without occurs check
unify(A,B):-
nonvar(A),var(B),A=B. % without occurs check
unify(A,B):-
compound(A),compound(B),
A=..[F|ArgsA],B=..[F|ArgsB],
unify_args(ArgsA,ArgsB).
unify_args([A|TA],[B|TB]):-
unify(A,B),
unify_args(TA,TB).
unify_args([],[]).```
Here is a partial implementation of something like the Martelli and Montanari unification algorithm described at https://en.wikipedia.org/wiki/Unification_(computer_science)#A_unification_algorithm. The comments for each part refer to the corresponding rewrite rule from the algorithm. Note that there is no need for an explicit conflict rule, we can just fail if no other rule applies.
% assuming a universe with function symbols g/2, p/2, q/2
% identical terms unify (delete rule)
unify(X, Y) :-
X == Y,
!.
% a variable unifies with anything (eliminate rule)
unify(X, Y) :-
var(X),
!,
X = Y.
% an equation Term = Variable can be solved as Variable = Term (swap rule)
unify(X, Y) :-
var(Y),
!,
unify(Y, X).
% given equal function symbols, unify the arguments (decompose rule)
unify(g(A, B), g(X, Y)) :-
unify(A, X),
unify(B, Y).
unify(p(A, B), p(X, Y)) :-
unify(A, X),
unify(B, Y).
unify(q(A, B), q(X, Y)) :-
unify(A, X),
unify(B, Y).
Examples:
?- unify(q(Y,g(a,b)), p(g(X,X),Y)).
false.
?- unify(q(Y,g(a,b)), q(g(X,X),Y)).
false.
?- unify(q(Y,g(a,a)), q(g(X,X),Y)).
Y = g(a, a),
X = a.
One or two things remain for you to do:
Generalize the decompose rule to deal with arbitrary terms. You might find the =.. operator useful. For example:
?- Term = r(a, b, c), Term =.. FunctorAndArgs, [Functor | Args] = FunctorAndArgs.
Term = r(a, b, c),
FunctorAndArgs = [r, a, b, c],
Functor = r,
Args = [a, b, c].
You will need to check if two terms have the same functor and the same number of arguments, and whether all corresponding pairs of arguments unify.
Find out if your professor would like you to implement the occurs check, and if yes, implement it.

Defining "let expressions" in Prolog

In many functional programming languages, it is possible to "redefine" local variables using a let expression:
let example =
let a = 1 in
let a = a+1 in
a + 1
I couldn't find a built-in Prolog predicate for this purpose, so I tried to define a let expression in this way:
:- initialization(main).
:- set_prolog_flag(double_quotes, chars).
replace(Subterm0, Subterm, Term0, Term) :-
( Term0 == Subterm0 -> Term = Subterm
; var(Term0) -> Term = Term0
; Term0 =.. [F|Args0],
maplist(replace(Subterm0,Subterm), Args0, Args),
Term =.. [F|Args]
).
let(A,B) :-
((D,D1) = (A1 is B1,C is B1);
(D,D1) = (A1=B1,C=B1)),
subsumes_term(D,A),
D=A,
replace(A1,C,B,B2),
call((D1,B2)).
main :- let(A = 1,(
writeln(A),
let(A is A+1,(
writeln(A),
let(A is A * 2,(
writeln(A)
))
))
)).
This implementation appears to incorrect, since some of the variables are bound before being replaced. I want to define an expression that would allow more than one variable to be "redefined" simultaneously:
main :- let((A = 1, B = 2), % this will not work with the let/2 predicate that I defined
let((A=B,B=A),(
writeln(A),
writeln(B)
))
).
Is it possible to implement a let expression in a way that allows several variables to be redefined at the same time?
The issue with defining let as a normal predicate is that you can't redefine variables that appear outside the outermost let. Here is my attempt at a more correct version, which uses goal expansion. (To me it makes sense, because as far as I know, in lisp-like languages, let cannot be defined as a function but it could be defined as a macro.)
%goal_expansion(let(Decl,OriginalGoal),Goal) :- %% SWI syntax
goal_expansion(let(Decl,OriginalGoal), _M, _, Goal, []) :- %%SICStus syntax
!,
expand_let(Decl,OriginalGoal,Goal).
expand_let(X, OriginalGoal, Goal) :-
var(X),
!,
replace(X,_Y,OriginalGoal,NewGoal),
Goal=(true,NewGoal).
expand_let(X is Decl, OriginalGoal, Goal) :-
var(X),
!,
replace(X,Y,OriginalGoal,NewGoal),
Goal=(Y is Decl,NewGoal).
expand_let(X = Decl, OriginalGoal, Goal) :-
var(X),
!,
replace(X,Y,OriginalGoal,NewGoal),
Goal=(Y = Decl,NewGoal).
expand_let([],OriginalGoal, Goal) :-
!,
Goal=OriginalGoal.
expand_let([L|Ls],OriginalGoal, Goal) :-
!,
expand_let_list([L|Ls],OriginalGoal,InitGoals,NewGoal),
Goal=(InitGoals,NewGoal).
expand_let((L,Ls),OriginalGoal, Goal) :-
!,
expand_let(Ls,OriginalGoal, SecondGoal),
expand_let(L,SecondGoal, Goal).
expand_let_list([],Goal,true,Goal).
expand_let_list([L|Ls],OriginalGoal,(Init,InitGoals),NewGoal):-
(
var(L)
->
replace(L,_,OriginalGoal,SecondGoal),
Init=true
;
L=(X=Decl)
->
replace(X,Y,OriginalGoal,SecondGoal),
Init=(Y=Decl)
;
L=(X is Decl)
->
replace(X,Y,OriginalGoal,SecondGoal),
Init=(Y is Decl)
),
expand_let_list(Ls,SecondGoal,InitGoals,NewGoal).
This is reusing the replace/4 predicate defined in the question. Note also that the hook predicate differs between Prolog versions. I am using SICStus, which defines goal_expansion/5. I had a quick look at the documentation and it seems that SWI-Prolog has a goal_expansion/2.
I introduced a different syntax for multiple declarations in a single let: let((X1,X2),...) defines X1, then defines X2 (so is equivalent to let(X1,let(X2,...))), while let([X1,X2],...) defines X1 and X2 at the same time (allowing the swap example).
Here are a few example calls:
test1 :- let(A = 1,(
print(A),nl,
let(A is A+1,(
print(A),nl,
let(A is A + 1,(
print(A),nl
))
))
)).
test2 :- A=2,let([A=B,B=A],(print(B),nl)).
test3 :- A=1, let((
A is A * 2,
A is A * 2,
A is A * 2
),(
print(A),nl
)),print(A),nl.
test4 :- let([A=1,B=2],let([A=B,B=A],(print(A-B),nl))).
test5 :- let((
[A=1,B=2],
[A=B,B=A]
),(
print(A-B),nl
)).
let is essentially a way of creating (inline to the source) a new, local context in which to evaluate functions (see also: In what programming language did “let” first appear?)
Prolog does not have "local contexts" - the only context is the clause. Variables names are only valid for a clause, and are fully visible inside the clause. Prolog is, unlike functional programs, very "flat".
Consider the main:
main :- let(A = 1,(
writeln(A),
let(A is A+1,(
writeln(A),
let(A is A * 2,(
writeln(A)
))
))
)).
Context being clauses, this is essentially "wrong pseudo code" for the following:
main :- f(1).
f(A) :- writeln(A), B is A+1, g(B).
g(A) :- writeln(A), B is A*2, h(B).
h(A) :- writeln(A).
?- main.
1
2
4
true.
The let doesn't really bring much to the table here. It seems to allow one to avoid having to manually relabel variables "on the right" of the is, but that's not worth it.
(Now, if there was a way of creating nested contexts of predicates to organize code I would gladly embrace that!).
Let's probe further for fun (and because I'm currently trying to implement the Monad Idiom to see whether that makes sense).
You could consider creating an explicit representation of the context of variable bindings, as if you were writing a LISP interpreter. This can be done easily with SWI-Prolog dicts, which are just immutable maps as used in functional programming. Now note that the value of a variable may become "more precise" as computation goes on, as long as it has any part that is still a "hole", which leads to the possibility of old, deep contexts getting modified by a current operation, not sure how to think about that.
First define the predicate to generate a new dict from an existing one, i.e. define the new context from the old one, then the code becomes:
inc_a(Din,Din.put(a,X)) :- X is Din.a + 1.
twice_a(Din,Din.put(a,X)) :- X is Din.a * 2.
main :- f(_{a:1}).
f(D) :- writeln(D.a), inc_a(D,D2), g(D2).
g(D) :- writeln(D.a), twice_a(D,D2), h(D2).
h(D) :- writeln(D.a).
The A has gone inside the dict D which is weaved through the calls.
You can now write a predicate that takes a dict and the name of a
context-modifying predicate ModOp, does something that depends on the context (like calling writeln/1 with the value of a), then modifies the
context according to ModOp.
And then deploy foldl/4 working over a list, not of objects, but of operations, or rather, names of operations:
inc_a(Din,Din.put(a,X)) :- X is Din.a + 1.
twice_a(Din,Din.put(a,X)) :- X is Din.a * 2.
nop(Din,Din).
write_then_mod(ModOp,DictFromLeft,DictToRight) :-
writeln(DictFromLeft.a),
call(ModOp,DictFromLeft,DictToRight).
main :-
express(_{a:1},[inc_a,twice_a,nop],_DictOut).
express(DictIn,ModOps,DictOut) :-
foldl(
write_then_mod, % will be called with args in correct order
ModOps,
DictIn,
DictOut).
Does it work?
?- main.
1
2
4
true.
Is it useful? It's definitely flexible:
?- express(_{a:1},[inc_a,twice_a,twice_a,inc_a,nop],_DictOut).
1
2
4
8
9
_DictOut = _9368{a:9}.
This is how you would type this in using Prolog syntax:
example(X, Y) :-
X = 1,
succ(X, Y).
If it is something else you are trying to achieve, you need to explain better. "How do I type it in Prolog" comes strictly after "What am I doing?"
Or is it that you really want this kind of syntactic nesting in Prolog? Could you provide a couple of examples where you think it is beneficial?
It's possible to define a let predicate that recursively replaces nested let expressions, so that local variables can be "redefined" without being renamed. This is one way to implement it:
:- initialization(main).
:- set_prolog_flag(double_quotes, chars).
replace(Subterm0, Subterm, Term0, Term) :-
( Term0 == Subterm0 -> Term = Subterm
; var(Term0) -> Term = Term0
; Term0 =.. [F|Args0],
maplist(replace(Subterm0,Subterm), Args0, Args),
Term =.. [F|Args]
).
replace_let(Term0, Term) :-
( [Term0,Term1] = [A,(A2 is B1, C2)],
(Pattern = (A1 is B1);Pattern = (A1 = B1)),
P1 = let(Pattern,C1),
subsumes_term(P1,A),
P1=A,
replace(A1,A2,C1,C2),
replace_let(Term1,Term)
; var(Term0) -> Term = Term0
; Term0 =.. [F|Args0],
maplist(replace_let, Args0, Args),
Term =.. [F|Args]
).
let(A,B) :- replace_let(let(A,B),C),call(C).
main :-
B = 3,
let(A is B+1,(
writeln(A),
let(A is A + 1,(
writeln(A),
C is A + 1,
let(A = C,(
writeln(A)
))
))
)).
This implementation still doesn't work with "simultaneous" variable definitions, but the replace/2 predicate could easily be modified to replace several variables simultaneously.

How to evaluate a variable with string value?

My code does perfect with numbers, but error with single quotation. I'm trying to write a foldl function. When i do foldl1(concat, ['a','b'], X), it reports like "ERROR: Arithmetic: 'ab/0' is not a function". what is the problem? prolog does not allow using is with string?
foldl1(P, [H], X) :-
X is H.
foldl1(P, [H|T], X) :-
foldl1(P, T, Y),
call(P, H, Y, Z),
X is Z.
is/2 evaluates the arithmetic expression to the right, and unifies the result with the term to the left. Unification is also performed against the head' arguments, so you can write a simplified foldl1/3 like
foldl1(_, [H], H).
foldl1(P, [H|T], Z) :-
foldl1(P, T, Y),
call(P, H, Y, Z).
test:
?- foldl1(plus,[1,2,3],R).
R = 6 ;
false.
?- foldl1(concat,[1,2,3],R).
R = '123' ;
false.
I would place a cut after the recursion base, since [H] and [H|T] where T=[] overlap, to avoid any last call - that would anyway fail - on eventual backtracking, like the redo induced by me, inputting ; after the expected first answer while the interpreter waits for my choices.
After the cut (hope you can easily spot where to place it) we get:
?- foldl1(plus,[1,2,3],R).
R = 6.
?- foldl1(concat,[1,2,3],R).
R = '123'.
Now the interpreter 'knows' there are no more answers after the first...
It's also possible to implement a foldl1/3 predicate using first-argument indexing to avoid spurious choice-points without cuts and that is also tail-recursive. From the Logtalk library meta object:
:- meta_predicate(foldl1(3, *, *)).
foldl1(Closure, [Head| Tail], Result) :-
fold_left_(Tail, Closure, Head, Result).
fold_left_([], _, Result, Result).
fold_left_([Arg| Args], Closure, Acc, Result) :-
call(Closure, Acc, Arg, Acc2),
fold_left_(Args, Closure, Acc2, Result).
Sample calls:
?- meta::foldl1(plus,[1,2,3],R).
R = 6.
?- meta::foldl1(concat,[1,2,3],R).
R = '123'.

Prolog addition on wrapped values

I wrote a test program with bindings (facts) between atoms and numbers.
bind(a, 3).
bind(b, 4).
bind(c, 5).
As part of a toy interpreter, I want to be able to perform additions on these atoms using Prolog's native arithmetic operators. For instance, I want to be able to run this query:
% val(X) is the value bound to X
?- X is val(a) + val(b).
X = 7.
However, I'm struggling to find a way to allow this addition. My first approach would have been this one:
% val(X, Y): Y is the value bound to X
val(X, Y) :- bind(X, Y).
% Make val an arithmetic function
:- arithmetic_function(val/1).
However, arithmetic_function/1 is no longer part of Prolog (or at least SWI-Prolog says it's deprecated), so I can't use it. Then I believed the best solution would be to overload the + operator to take this into account:
% val(X, Y): Y is the value bound to X
val(val(X), Y) :- bind(X, Y).
% Overload the + operator
+(val(_X, XVal), val(_Y, YVal)) :- XVal + YVal.
But here I've got my syntax all messed up because I don't really know how to overload a native arithmetic operation. When I type in the sample query from before, SWI-Prolog says ERROR: Arithmetic: ``val(a)' is not a function.
Would you have hints about a possible solution or a better approach or something I missed?
From the docs, I tought you should use function_expansion/3.
But I'm unable to get it to work, instead, goal_expansion could do, but isn't very attractive... for instance, if you save the following definitions in a file bind.pl (just to say)
:- module(bind, [test/0]).
:- dynamic bind/2.
bind(a, 3).
bind(b, 4).
bind(c, 5).
% :- multifile user:goal_expansion/2.
user:goal_expansion(val(X), Y) :- bind(X, Y).
user:goal_expansion(X is Y, X is Z) :- expand_goal(Y, Z).
user:goal_expansion(X + Y, U + V) :- expand_goal(X, U), expand_goal(Y, V).
test :-
X is val(a) + val(b), writeln(X).
and consult it, you can run your test:
?- test.
7
edit
after Paulo suggestion, here is an enhanced solution, that should work for every binary expression.
user:goal_expansion(X is Y, X is Z) :- expr_bind(Y, Z).
expr_bind(val(A), V) :- !, bind(A, V).
expr_bind(X, Y) :-
X =.. [F, L, R], % get operator F and Left,Right expressions
expr_bind(L, S), % bind Left expression
expr_bind(R, T), % bind Right expression
Y =.. [F, S, T]. % pack bound expressions back with same operator
expr_bind(X, X). % oops, I forgot... this clause allows numbers and variables
having defined user as target module for goal_expansion, it works on the CLI:
?- R is val(a)*val(b)-val(c).
R = 7.
edit
now, let's generalize to some other arithmetic operators, using the same skeleton expr_bind uses for binary expressions:
user:goal_expansion(X, Y) :-
X =.. [F,L,R], memberchk(F, [is, =<, <, =:=, >, >=]),
expr_bind(L, S),
expr_bind(R, T),
Y =.. [F, S, T].
and unary operators (I cannot recall no one apart minus, so I show a simpler way than (=..)/2):
...
expr_bind(-X, -Y) :- expr_bind(X, Y).
expr_bind(X, X).
Now we get
?- -val(a)*2 < val(b)-val(c).
true.
One way to do it is using Logtalk parametric objects (Logtalk runs on SWI-Prolog and 11 other Prolog systems; this makes this solution highly portable). The idea is to define each arithmetic operation as a parametric object that understands an eval/1 message. First we define a protocol that will be implemented by the objects representing the arithmetic operations:
:- protocol(eval).
:- public(eval/1).
:- end_protocol.
The basic parametric object understands val/1 and contains the bind/2 table:
:- object(val(_X_), implements(eval)).
eval(X) :-
bind(_X_, X).
bind(a, 3).
bind(b, 4).
bind(c, 5).
:- end_object.
I exemplify here only the implementation for arithmetic addition:
:- object(_X_ + _Y_, implements(eval)).
eval(Result) :-
_X_::eval(X), _Y_::eval(Y),
Result is X + Y.
:- end_object.
Sample call (assuming the entities above are saved in an eval.lgt file):
% swilgt
...
?- {eval}.
% [ /Users/pmoura/Desktop/eval.lgt loaded ]
% (0 warnings)
true.
?- (val(a) + val(b))::eval(R).
R = 7.
This can be an interesting solution if you plan to implement more functionality other than expression evaluation. E.g. a similar solution but for symbolic differentiation of arithmetic expressions can be found at:
https://github.com/LogtalkDotOrg/logtalk3/tree/master/examples/symdiff
This solution will also work in the case of runtime generated expressions (term-expansion based solutions usually only work at source file compile time and at the top-level).
If you're only interested in expression evaluation, Capelli's solution is more compact and retains is/2 for evaluation. It can also be made more portable if necessary using Logtalk's portable term-expansion mechanism (but note the caveat in the previous paragraph).
This is perhaps not exactly what I was looking for, but I had an idea:
compute(val(X) + val(Y), Out) :-
bind(X, XVal),
bind(Y, YVal),
Out is XVal + YVal.
Now I can run the following query:
?- compute(val(a) + val(c), Out).
Out = 8.
Now I need to define compute for every arithmetic operation I'm interested in, then get my interpreter to run expressions through it.

Function not in prolog

sibling(X, Y):- father(Z, X), father(Z, Y), not (X=Y).
sister(X, Y):- father(Z, X), father(Z, Y), female(X).
brother(X, Y):- father(Z, X), father(Z, Y), male(X).
i'm having a bit problem with using the not function. i've tried not X=Y. but to no avail, the sibling rule still produce error.
if i were to delete the not x=y, the output will be a bit kind of "ugly".
how should i write the not function?
The ISO predicate implementing not provable is called (\+)/1.
However, as #coder explains in the comments, it is much better to use dif/2 to express that two terms are different.
dif/2 is a pure predicate that works correctly in all directions, also if its arguments are not yet instantiated.
For example, with (\+)/1, we get:
?- \+ (X = Y ).
false.
No X and Y exist that satisfy this goal, right? Wrong:
?- X = a, Y = b, \+ (X = Y ).
X = a,
Y = b.
In contrast, with dif/2:
?- dif(X, Y).
dif(X, Y).
and in particular:
?- X = a, Y = b, dif(X, Y).
X = a,
Y = b.
See prolog-dif for more information. dif/2 is with us since the very first Prolog system. I strongly recommend you use it.
SWI Prolog has no notoperator. it can be used as a regular compound term, e.i. not(X).
It must be no space between functor and open parenthesis:
foo( argument list ).
This is the cause of the error.
SWI Prolog suggests ISO-standard replacement for not/1: (\+)/1

Resources