Planning with Numerical Values and PROLOG - prolog

I am trying to use this Planner https://www.cs.unm.edu/~luger/ai-final/code/PROLOG.planner.html .
I want to search a space with a numerical value. Below you can see my implementation of sum that is tested and seems to work. Then there are my two moves, plus and minus, where I delete the value(X) and add value(Y) where Y is X +- 1 respectively.
If I now consult the planner (last statement with go), I do not get the expected result of plus() but instead false as the planner does not find a way from value(2) to value(3).
sum(X,Y,Z) :- Z is X+Y.
move(plus(), [value(X)], [del(value(X)), sum(X,1,Y) , add(value(Y))]).
move(minus(), [value(X)], [del(value(X)), sum(X,-1,Y) , add(value(Y))]).
go([value(2)], [value(3)]).

The predicate move(Action, Preconditions, Effects) describes the changes caused by the execution of Action in a state where the conditions established in list Preconditions hold. In a fact for this predicate, the list Effects must consist only of terms of the form add(Fluent) and del(Fluent). So, I think the correct definition of actions plus and minus should be as follows:
move(plus, [value(X)], [del(value(X)), add(value(Y))]) :- Y is X + 1.
move(minus, [value(X)], [del(value(X)), add(value(Y))]) :- Y is X - 1.

Related

Prolog: Chaining multiple rules

So I am an absolute beginner in Prolog. I am learning recursion at the moment and this is what I got until now on my task.
dance(start).
dance(forward(T)) :- dance(T).
dance(backward(T)) :- dance(T).
count(start,0,0).
count(forward(X),succ(Y),Z) :- count(X,Y,Z).
count(backward(X),Y,succ(Z)) :- count(X,Y,Z).
greater(succ(0),0).
greater(succ(Y),succ(Z)):-greater(Y,Z).`
Summary of what this is supposed to do: There can be dances starting with the "start" and then either forward or backward addition per recursion. In count, I managed to be able to count the amount of forward and backward in a given sequence and save them in the "succ" notation and in greater I want to compare two of such "succ" numbers. And greater shall be true if the first argument is larger (consists of more "succs" than the second argument.
Now my task is to write a new rule "more(X)" which is true if the sequence X (build from dance) has more forward than backward in it. I think I have all the subtasks I need for it but now I am helpless with chaining them together because until now I only had 2 rules with the same amount of parameters but in this case, I got one with one, two, and three parameters. The solution should be something like this
more(X):-greater(Y,Z)
but how do I get my "succ" umbers from "count" to "greater" and the given sequence X to "count"? I do have to change some of the rules otherwise count is never called, right?
So it should be more like more(X):-greater(count(X,Y,Z)) ?
But like this, I would have to change the greater rules to be able to "get" this type of parameter.
Example query ?- more(backward(forward(start))).
false.
?- more(forward(start)).
true.
Your dance/1 and count/3 predicates seems correct.
If you want "greater" where greater(X, Y) means that X is greater than Y, you'd write:
greater(succ(_), 0).
greater(succ(X), succ(Y)) :- greater(X, Y).
Your solution checks if X is exactly one greater than Y.
Note that nothing is greater than 0.
If you implement more/1 in terms of count/3 and greater/2, you'd write:
more(X) :-
count(X, Forward, Backward),
greater(Forward, Backward).
So it should be more like more(X):-greater(count(X,Y,Z)) ?
No, you are writing that as if it is Python and greater(count(X,Y,Z)) will call greater(...) on the return from count. Prolog predicates are not function calls, and count(...) does not return anything in that way.
The result of the count is Y and Z. (These names you are using could be clearer). Do the count, and then after, use the Y and Z for something else.
count(X,Y,Z),
greater(Y,Z)
The comma being AND; "this code works if count of X is Y and Z AND Y is greater than Z".

How to set mutually exclusive probabilities in Problog?

A person X can either be inpatient or outpatient.
Given the fact location(X,outpatient) how can Problog infer that the probability of location(X,inpatient) is 0?
For example I want a side effect of:
person(1).
location(1,inpatient).
dependent(1,opioids).
receive(1,clonidine).
query(detoxification(1,opioids,success)).
to be an inference that location(1,outpatient) has zero probability.
If I write location(X,outpatient);location(X,inpatient)., all queries return both with a probability of 1.
If I write P::location(X,outpatient);(1-P)::location(X,inpatient). that gives an error because I haven't specified a value for P. If I specify a value for P, that value is never updated (as expected because Prolog treats variables as algebraic variables and I haven't told Problog to update P.
If I write location(X,outpatient) :- \+ location(X,inpatient). I create a negative cycle, which I have to if I am to specify the inverse goal.
One solution:
P::property(X,location,inpatient);(1-P)::property(X,location, outpatient) :-
inpatient(X),
P is 1.
P::property(X,location,outpatient);(1-P)::property(X,location, inpatient) :-
outpatient(X),
P is 1.
P::inpatient(X);(1-P)::outpatient(X) :-
property(X,location,inpatient),
P is 1.
P::outpatient(X);(1-P)::inpatient(X) :-
property(X,location,outpatient),
P is 1.
This binds inpatient/1 to property/3 for the property of location with value inpatient.

Average of a student using prolog

I know this looks a bit silly, but I'm trying to find the average of a given student using Prolog, here's my piece of code:
score( jason , math101 , 90 ).
score( sami , math102 , 67 ).
score( smith , phys101 , 82 ).
score( sami , chem101 , 88 ).
do(X,S,I) :-
score(X,_,B) ,
write(B) ,
S is S+B ,
I is I+1 ,
write(I) ,
fail.
start :-
read(X) ,
do(X,0,0)
.
I'm trying to do it using recursion, the problem is that I (stands for index) and S(stands for sum) wont increment ! what did I do wrong? thanks !
You code has some problems:
Expressions like S is S+B don't work in prolog. Once a variable is unified with a term (assigned a value), it ceases to be variable: it becomes that with which it was unified. Hence the name unification.
Your do/3 predicate will always fail. It looks like you are forcing backtracking via fail/0 and expecting your counters to be incremented as you go. Prolog doesn't work like that: As you backtrack, unification is undone, so even if you'd managed to increment your S and B variables, the increment would have been rolled back as you backtracked through it.
You are invoking your do/3 predicate with S and B already unified to a value (0). So your is/2 expressions are the exact equivalent of this:
0 is 0+B , % S is S+B ,
0 is 0+1 , % I is I+1 ,
You can see how that won't work.
What you need to do is collect the scores for each student as a list. Since you can only get them one at a time via backtracking, building that list is a little difficult. If you tried to build that list via recursion, on each recursion you find yourself starting over from ground zero. Conveniently, Prolog offers a set of predicates for collecting data from all solutions to a goal as a list:
setof/3 finds the set of all solutions to a goal, presenting them as a list.
findall/3 finds the bag of all solutions to a goal, presenting them as a list.
bagof/3 finds the bag of all solutions to a goal, presenting them as a list.
Notes: findall/3 and bagof/3 are similar but different: to quote the docs, "findall/3 is equivalent to bagof/3 with all free variables bound with the existential operator ^". You'll probably need to play with it a bit to grok just what that means. The difference between set and bag is that by definition, sets are unique and have no duplicates. Bags, on the other hand are not necessarily unique and may contain duplicates.
At any rate, you're interested in bagof/3.
The first thing to do is decompose your problem into simple pieces. The first thing you need to do is compute the average score for a single student. to do that you need:
The student's name,
The list of individual scores for that student, and
a way to compute the arithmetic mean from a list of numbers.
So, let's tackle each of this in turn. The 1st two problems are solved with bagof/3. This:
bagof(Score,score(Student,_,Score),Scores) .
will find the list of scores for a student, unifying Student with the student's name and Scores with the list of individual scores. On backtracking it will successively do the same for all students.
That takes care of the first two simple problems. The 3rd problem isn't much more difficult. Here we will introduce two concepts that are integral to prolog: recursion and the use of accumulators to develop a value. we have to do this because as noted, in Prolog, all variables are local and can only be assigned a value once. So to develop a value, we have to recursively carry the state around that we need. A common idiom is the use of helper predicates that do all the work and are invoked by the "public" predicate that the user actually uses.
We can compute the arithmetic mean of a list of numbers like this:
compute_mean( Ns, M ) :- % to compute the mean of a list of numbers,
compute_mean( Ns , 0 , 0 , M ) % - just invoke the helper with the counters seeded as zero.
. % Also easy!
compute_mean( [] , S , N , M ) :- % if the source list is exhausted,
N > 0 , % - and we have a non-zero N (division by zero being undefined),
M is float(S) / float(N) % - compute the arithmetic mean, casting both counters to floats
. % otherwise...
compute_mean( [X|Xs] , S , N , M ) :- % if the source list isn't yet exhausted,
S1 is S+X , % - add the data point to the running total,
N1 is N+1 , % - increment the count of data points,
compute_mean(Xs,S1,N1,M) % - and recurse down on the tail of the list.
. % Easy!
That's all we need to compute the average score for a student, and on backtracking, successively do so for all students. Just put the two things together:
mean_score( Student , Mean ) :- % to compute the mean score for a student,
bagof( Mean , score(Student,_,Score) , Scores ) , % - get the bag of the scores for the student
compute_mean( Scores , Mean ) % - and then compute the mean score
. % Easy!
Once you have that, you could then just say something like this:
list_mean_scores_for_all_students :-
mean_score(Student,Mean) ,
write( Student:Mean) , nl ,
fail .
list_mean_scores_for_all_students .
The problem with the above is that list_mean_scores_for_all_students/0 will always succeed, even if something untowards happened. So, you might want to use findall/3 to collect them all at once and then process them.
Then we can say:
list_mean_scores_for_all_students :-
findall( Student:Mean , mean_score(Student,Mean) , Results ) ,
log_results(Results)
.
log_results( [] ) .
log_results( [S:M|Xs] ) :-
write(S:M),nl ,
log_results(Xs)
.
If something fails, list_means_scores_for_all_students/0 will fail.

using arithmetic operations in Prolog

I have the following code:
position(0,0).
move(f):-
position(X,Y),
number(X),
number(Y),
Y is Y+1,
X is X+1.
but when i call move(f) it returns false. number(X) and number(Y) returns true but whem i add the other two lines the function doesn't work. what's the problem?
Elaborating on some of the comments your question has received, variables in Prolog stand for a possible instantiation of a single value, just like variables in mathematics and mathematical logic, and once they are instantiated within a context they must remain consistent. If we're dealing with a formula 0 = (a + b) - (a + b), we know that it can only express its intended sense if any value assigned to the first a is also assigned to the second. That is, we can substitute any value for a, but it must be the same value throughout. Prolog works with variables in this same way. If x = x + 1, then 2 = 3; but then math would be broken.
Addressing mat's caution against using dynamic predicates, here is a possible way of handling moves, but accomplished by passing around a list of previous moves. With this method, the most recent move will always be the first element of List in the compound term moves(List).
Supposing the current history of moves is as follows:
moves([position(0,0), position(0,1), position(1,1)]).
move/3 takes a direction, a complex term representing the previous moves, and tells us what the updated list of moves is.
move(Direction, moves([From|Ms]), moves([To,From|Ms])) :-
move_in_direction(Direction,From,To).
move_in_direction/3 takes a direction, and a position, and tells us what the next position in that direction is:
move_in_direction(left, position(X1,Y1), position(X2,Y1)) :- X2 is X1 - 1.
move_in_direction(right, position(X1,Y1), position(X2,Y1)) :- X2 is X1 + 1.
move_in_direction(up, position(X1,Y1), position(X1,Y2)) :- Y2 is Y1 + 1.
move_in_direction(down, position(X1,Y1), position(X1,Y2)) :- Y2 is Y1 - 1.
Notice that, using this method, you get a back-trackable history of moves for free. I'd imagine you could use this in interesting ways -- e.g. having the player explore possible series of moves until a certain condition is met, at which point it commits or backtracks. I'd be interested to know what kind of solution you end up going with.

Sum of a list in prolog

I'm reading 'the art of prolog' book and I found an exercise that reads 'Define the relation sum(ListOfIntegers,Sum) which holds if Sum is the sum of the ListOfIntegers, without using any auxiliary predicate' .I came up with this solution:
sum([],Sum).
sum([0|Xs], Sum):-sum(Xs, Sum).
sum([s(X)|Xs], Sum):-sum([X|Xs],s(Sum)).
Which does not work exactly as I would want it to.
?- sum([s(s(0)),s(0),s(s(s(0)))],X).
true ;
false.
I was expecting X to be
s(s(s(s(s(s(0))))))
I thought that the problem is that I have to 'initialize' Sum to 0 in the first 'iteration' but that would be very procedural and unfortunately I'm not quite apt in prolog to make that work.
Any ideas or suggestions?
Your first clause should read
sum([], 0).
With that change, the vacuous true return goes away and you're left with one problem: the third clause reverses the logic of summation. It should be
sum([s(X)|Xs], s(Sum)) :- sum([X|Xs], Sum).
because the number of s/1 terms in the left argument to sum/2 should be equal to the number of them in the right argument.
The best way to localize the problem is to first simplify your query:
?- sum([0],S).
true.
?- sum([],S).
true.
Even for those, you get as an answer that any S will do. Like
?- sum([],s(s(0))).
true.
Since [] can only be handled by your fact, an error must lie in that very fact.
You stated:
sum([], Sum).
Which means that the sum of [] is just anything. You probably meant 0.
Another error hides in the last rule... After fixing the first error, we get
?- sum([0],Sum).
Sum = 0.
?- sum([s(0)],Sum).
false.
Here, the last clause is responsible. It reads:
sum([s(X)|Xs], Sum):-sum([X|Xs],s(Sum)).
Recursive rules are relatively tricky to read in Prolog. The simplest way to understand them is to look at the :- and realize that this should be an arrow ← (thus a right-to-left arrow) meaning:
provided, that the goals on the right-hand side are truewe conclude what is found on the left-hand side
So, compared to informal writing, the arrows points into the opposite direction!
For our query, we can consider the following instantiation substituting Xs with [] and X with 0.
sum([s(0)| [] ], Sum) :- sum([0| []],s(Sum)).
So this rule now reads right-to-left: Provided, sum([0],s(Sum)) is true, ... However, we do know that only sum([0],0) holds, but not that goal. Therefore, this rule never applies! What you intended was rather the opposite:
sum([s(X)|Xs], s(Sum)):-sum([X|Xs],Sum).
I'm not really following your logic, what with all the seemingle extraneous s(X) structures floating about.
Wouldn't it be easier and simpler to do something like this?
First, define your solution in plain english, thus:
The sum of an empty list is 0.
The sum of a non-empty list is obtained by adding the head of the list to the sum of the tail of the list.
From that definition, the prolog follows directly:
sum( [] , 0 ) . % the sum of an empty list is 0.
sum( [X|Xs] , T ) :- % the sum of an non-empty list is obtained by:
sum( Xs , T1 ) , % - first computing the sum of the tail
T is X + T1 % - and then, adding that the to head of the list
. % Easy!

Resources