3

I need to simplify identities in prolog (e.g. x+0 = x, x-x=0, etc.). For this I need to replace parts of the expression (say x+0 by x).

Can you please help me in doing the replacement?

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
  • like an expression x+y*1+0 should return x+y – user3026485 Nov 24 '13 at 04:41
  • 2
    won't you mind post the code you've written so far? – rano Nov 24 '13 at 09:13
  • This would be my fist step. i am a newbie. i don't know how to start. please help. – user3026485 Nov 24 '13 at 11:13
  • 1
    For your first step I suggest go studying prolog for example from Ivan Bratko's Prolog Programming for Artificial Intelligence – rano Nov 24 '13 at 11:18
  • 1
    If you have more examples, you should add them to (edit) your original problem rather than add them with a comment so that they're more visible with and part of your problem statement. Also, I agree with @rano that it sounds like you need to go look up some examples first to get started. A first step is deciding how you want to represent the expressions in prolog. – lurker Nov 24 '13 at 12:19

2 Answers2

1

A neat thing about Prolog is that you can deconstruct an arithmetic expression pretty easily. Your basic template is going to look like this:

simplify(X, X)       :- number(X) ; atom(X) ; var(X).    
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 doesn't really do anything except recursively walk the expression. We're recursively calling simplify on both sides of the operator so that the simplification patterns are matched regardless of where they occur in the expression. But for now it appears to do nothing:

?- simplify(3+X*y, Q).
Q = 3+X*y

Think of it as your empty loop. With this in hand, you can start dealing with your identities by putting "special cases" above this traversal. The identities are one thing you can handle this way:

simplify(1*X, X1) :- simplify(X, X1).
simplify(X*1, X1) :- simplify(X, X1).
simplify(0+X, X1) :- simplify(X, X1).
simplify(X+0, X1) :- simplify(X, X1).

Trying it out:

?- simplify(x*1+0, Q).
Q = x 

And you can see why we're using a recursive routine here:

?- simplify(x+(y+(z*43)+0)*1, Q).
Q = x+(y+z*43)

Even though the +0 is deeply nested within the structure, it is removed. We could do some more drastic simplifications too:

simplify(_*0, 0).
simplify(0*_, 0).

No need for recursion here, the whole subexpression is basically deleted:

?- simplify(x+(y+(z*43)+1)*0, Q).
Q = x+0 ;

You can do some more interesting things as well:

simplify(X*Y+Z*Y, Y1*(X1+Z1)) :- 
  simplify(X, X1), simplify(Y, Y1), simplify(Z, Z1).

Trying it out:

?- simplify(34*z+17*z, X).
X = z* (34+17) 

?- simplify(34*z+z*17, X).
X = 34*z+z*17 

This second example here reveals that there is a limitation to this type of processing. You don't want to have to give every permutation of your templates. If you want to make them more intelligent you're probably going to have to either adopt a more intelligent intermediate representation, or a more intelligent means of applying templates than simple unification. Unification is great, but it doesn't understand the commutative or associative properties.

To go further, you're probably going to want to dig a little deeper into Prolog. This type of problem is studied in most Prolog books; I'm partial to Programming Prolog and Art of Prolog but I hear Bratko's book is amazing. I'd recommend you get your hands on one of these and start digging through it. Don't hesitate to ask more questions though!

Daniel Lyons
  • 22,421
  • 2
  • 50
  • 77
  • 1
    If the expression has any free variables, this `simplify/2` predicate may unify variables that were not intended to be unified. It might be better to match the patterns using [`subsumes_term/2`](https://stackoverflow.com/a/64617275/975097). – Anderson Green Nov 01 '20 at 00:29
  • @AndersonGreen Thanks for that, I haven't used `subsumes_term/2` before but it does look like it would be very useful! – Daniel Lyons Nov 02 '20 at 16:55
0

It is possible to replace subterms in an expression using =../2. Based on this predicate, I wrote a predicate that replaces all occurrences of a pattern in a term:

:- initialization(main).
:- set_prolog_flag('double_quotes','chars').

main :- Input=((X+0)=(Y+0)-(Z+0)),replace(A+0,A,Input,Output),writeln(Input),writeln(Output).

replace(Subterm0_, Subterm_, Term0, Term) :-
        copy_term([Subterm0_,Subterm_],[Subterm0,Subterm]),
        (   Term0=Subterm0 -> (Term = Subterm)
        ;   var(Term0) -> Term = Term0
        ;   Term0 =.. [F|Args0],
            maplist_(Args0, Args, replace(Subterm0,Subterm)),
            Term =.. [F|Args]
        ).

maplist_([], [], _).
maplist_([Elem1|Tail1], [Elem2|Tail2], replace(A,B)) :-
    copy_term([A,B],[A1,B1]),
    Goal=replace(A1,B1),
    writeln(Goal),
    call(Goal, Elem1, Elem2),
    maplist_(Tail1, Tail2, Goal).

In this case, the input is (X+0)=(Y+0)-(Z+0) and the output is X=Y-Z.

Anderson Green
  • 30,230
  • 67
  • 195
  • 328