Obtain intermediate variables assignments - prolog

I am trying to use Prolog to represent state of a room.
I have a set of rules and a set of facts, but sometimes some of the facts are
not defined. For instance, temperature in a room can decrease due to either
cooling or opening a window, but sometimes I do not have a window sensor.
% Rules
temperature_trend(decrease) :-
cooling(on).
temperature_trend(decrease) :-
window(open).
% Facts
cooling(off).
%window(close). % Unknown, I do not have a window sensor
% Main
main() :-
temperature_trend(decrease).
If I run this program I would get an undefined procedure error. I can deal with
this by explicitly setting the window
status to "anything" with window(W). (I programmatically prepare the Prolog
source, so this is quite easy).
Now the query temperature_trend(decrease)
would succeed because window(W) would lead to window(open). However, in this
case I want to know that W = open.
Is there a way to return the variable assignments for this fact? Or maybe am I approaching the problem in the wrong way?
Note that the
rule tree could be arbitrarily deep, for instance I could add a new rule next_temperature(lower) :- temperature_trend(decrease). and I still
want to know that next_temperature(lower) succeeds only by setting W = open. Terms are also more complex
because they also have a time index (T = 232).
Maybe one option would be to return a list of assignments, which would be empty
if all facts were known.

Write a meta-interpreter that gives you what is true, e.g.,
prove(Goal, True) :-
phrase(prove(Goal), True).
prove(true) -->
!.
prove((A,B)) -->
!,
prove(A),
prove(B).
prove((A;B)) -->
!,
( prove(A)
; prove(B)
).
prove(Fact) -->
[Fact],
{ clause(Fact, Body) },
prove(Body).
Now, given window(_), we get:
?- prove(temperature_trend(decrease), L).
L = [temperature_trend(decrease), window(open)].
Lots of variations are possible!

Related

I want to sum in SWI-Prolog depending on certain user answers

I need to sum in a variable depending on certain user answers and I'm starting to know Prolog syntax and paradigm.
Right now, I can read user data, and print it too, but I lack a way to accumulate the results, because right now the results are inconsistent.
What I have now is:
inicio :-
write('¿You have overweight yes/no?'),
read(R1),
write('¿Are you a smoker yes/no?'),
read(R2),
write('¿Do you have some direct relative with diabetes?'),
read(R3),
Risk is 0,
( R1 = yes -> Risk is Risk + 2 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]),
( R2 = yes -> Risk is Risk + 1 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]),
( R3 = yes -> Risk is Risk + 3 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]).
imprimir([]).
imprimir([Term| Terms]) :-
write(Term),
imprimir(Terms).
I'm going to show you a fundamentally different way of approaching this program that leverages Prolog a little better. First, let's make a table of the penalties. Making tables for your program's configuration is often a useful thing to do:
risk_penalty(overweight, 2).
risk_penalty(smoker, 1).
risk_penalty(diabetes, 3).
Now that we have a uniform way of thinking about the problem, let's see if we can make a uniform way of getting information from the user. Let's use the dynamic store to keep track of what the user has told us, because it will simplify querying later:
:- dynamic risk/2.
ask(Prompt, Fact) :-
format('~a [yes/no]> ', [Prompt]),
read(Response),
assertz(risk(Fact, Response)).
Now we have a little predicate we can use to interview the user. This kind of print-read-assert function is pretty common in small expert systems like yours, because it helps you separate the logic of the system from its front-end. When you do ask('Do you have X?', has_x), the dynamic store will either receive risk(has_x, yes) or risk(has_x, no) depending on which the user has entered. It also gives you a natural place to make the user input more robust, by checking it and re-asking if you get something weird.
Now we can do your initial loop a little more cleanly:
inicio :-
ask('Are you overweight?', overweight),
ask('Are you a smoker?', smoker),
ask('Do you have some direct relative with diabetes?', diabetes).
This just does the interview portion. Now if you run through it once, say answering "yes", "no", "yes", then the database will contain these facts:
?- risk(Factor, Response).
Factor = overweight,
Response = yes ;
Factor = smoker,
Response = no ;
Factor = diabetes,
Response = yes.
What we need to do now is select out "yes" factors and then look up their penalties and add them up. To do this, we can use findall/3, which takes a Template, a Goal, and gives back a result list:
?- findall(risk(Factor, Response), risk(Factor, Response), Responses).
Responses = [risk(overweight, yes), risk(smoker, no), risk(diabetes, yes)].
As you can see, I used the same template and goal here, just to see all the results, but we can put "yes" in to filter it down to just the risk factors we care about:
?- findall(risk(Factor), risk(Factor, yes), Responses).
Responses = [risk(overweight), risk(diabetes)].
Now you can see that the Template (first argument) is just some arbitrary structure populated with the variables that findall/3 found by running Goal, the second argument. So we could also just obtain the list of penalty values, if we look them up inside the Goal query. Like this:
?- findall(Penalty, %% <- template
(risk(Factor, yes), risk_penalty(Factor, Penalty)), %% <- goal
Penalties). %% <- result
Penalties = [2, 3].
We can then follow this with just sumlist/2 to add everything up:
?- findall(Penalty,
(risk(Factor, yes), risk_penalty(Factor, Penalty)),
Penalties),
sumlist(Responsa, Score).
Responsa = [2, 3],
Score = 5.
Now we can finish the inicio/0 predicate:
inicio :-
retractall(risk(_, _)),
ask('Are you overweight?', overweight),
ask('Are you a smoker?', smoker),
ask('Do you have some direct relative with diabetes?', diabetes)
findall(Penalty,
(risk(Factor, yes), risk_penalty(Factor, Penalty)), Penalties),
sumlist(Penalties, Score),
format('The result is ~a~n', [Score]).
This now looks like this when run:
?- inicio.
Are you overweight? [yes/no]> yes.
Are you a smoker? [yes/no]> |: no.
Do you have some direct relative with diabetes? [yes/no]> |: yes.
The result is 5
true.
I hope you find the result pleasing to look at, much less procedural, and easier to modify and maintain.

Compile time testfor 'atoms'

Completely new to prolog. Interesting journey so far in trying to change how I think, so appreciate any help here.
I am trying to assert facts for a pre-defined set of names. For example, assume I have a a set of people [alice, bob, ...] in one file. I would like to assert facts about these folks in other files, but want to make sure that these folks exist and that is checked when the facts are loaded/compiled(?).
For example, assume I don't have 'chuck' in the list and I make an assertion:
user: swipl app.pl
?- full_name(chuck, "Charlie Steel").
should result in an error.
What is the best way I can do this?
So, here's the code I came up with:
person(deborah).
person(tony).
read_my_file(Filename) :-
open(Filename, read, In),
read_my_file1(In),
close(In).
read_my_file1(In) :-
read(In, Term),
( Term == end_of_file
-> true
; assert_or_abort(Term),
read_my_file1(In)
).
assert_or_abort(Term) :-
( full_name(Person, Name) = Term
-> ( person(Person)
-> assertz(full_name(Person, Name))
; format(user, '~w is not a person I recognize~n', [Person])
)
; format(user, '~w is not a term I know how to parse~n', [Term])
).
The trick here is using read/2 to obtain a Prolog term from the stream, and then doing some deterministic tests of it, hence the nested conditional structure inside assert_or_abort/1. Supposing you have an input file that looks like this:
full_name(deborah, 'Deborah Ismyname').
full_name(chuck, 'Charlie Steel').
full_name(this, has, too, many, arguments).
squant.
You get this output:
?- read_my_file('foo.txt').
chuck is not a person I recognize
full_name(this,has,too,many,arguments) is not a term I know how to parse
squant is not a term I know how to parse
true.
?- full_name(X,Y).
X = deborah,
Y = 'Deborah Ismyname'.

Prolog dict predicate matching

Given this program, why am I forced to define every atom in the predicate, even if they're anonymous. Why is it that undefined variables in a dict predicate aren't thought of as anonymous?
funt2(X) :-
X = point{x:5, y:6}.
evalfunt(point{x:5, y : 6}) :-
write('hello world!').
evalfunt(point{x:_, y : _} ) :-
write('GoodBye world!').
Why can't I just say
evalfunt(point{x:5}) :-
write('GoodBye world!').
^that won't match, by the way.
I may as well just use a structure if I have to define every possible value in the dict to use dicts.
What's the motivation here? Can I do something to make my predicate terse? I'm trying to define a dict with 30 variables and this is a huge roadblock. It's going to increase my program size by a magnitude if I'm forced to define each variables (anonymous or not).
Dict is just a complex data type, like tuple, which has data AND structure. If you have, for example two facts:
fact(point{x:5, y:6}).
fact(point{x:5}).
Then the query
fact(point{x:_}).
will match the second one, but not the first one.
And the query
fact(point{x:_, y:_}).
Will match the first one, but not the second.
Now, if you want to match facts of the form fact(point{x:_, y:_, z:_}) only by one specific field, you can always write a helper rule:
matchByX(X, P) :- fact(P), P=point{x:X, y:_, z:_}.
So having facts:
fact(point{x:5, y:6, z:1}).
fact(point{x:1, y:2, z:3}).
fact(point{x:2, y:65, z:4}).
and quering
matchByX(1, P).
will return:
P = point{x:1, y:2, z:3}
UPDATE:
Moreover, in SWI-Prolog 7 version the field names can be matched as well, so it can be written in much more generic way, even for facts with different structures:
fact(point{x:5, y:6, z:1}).
fact(point{x:1, y:2}).
fact(point{x:2}).
fact(point{x:2, y:2}).
matchByField(F, X, P) :- fact(P), P.F = X.
So query:
?- matchByField(x, 2, P).
P = point{x:2} ;
P = point{x:2, y:2}.
I was able to accomplish what I needed by doing the following
checkiffive(Y) :-
get_dict(x, Y, V), V=5.
You need to use the built in methods for unifying values from a dict.
Described in chapter 5.4 of the SWI prolog reference
http://www.swi-prolog.org/download/devel/doc/SWI-Prolog-7.1.16.pdf

Retracting and asserting to another file in Prolog

I'm trying to retract and assert a fact in another file. One (fruit1.pl) contains a couple of facts, and another (fruit.pl) contains a predicate start which designates which fact that another predicate insert_fruit will update:
fruit1.pl
fruit(apple, [[2, yellow], [1, brown]]).
fruit(orange, [[3, orange], [2, orange]]).
fruit.pl
:- dynamic fruit/2.
start :-
consult('fruit1.pl'),
File = 'fruit1.pl',
Name = apple,
Price = 2,
Color = red,
insert_fruit(File, Name, Price, Color).
insert_fruit(File, Name, Price, Color) :-
open(File, update, Stream),
retract(fruit(Name, Information)),
assert(fruit(Name, [[Price, Color]|Information])),
close(Stream).
However insert_fruit is not working as intended, as I believe it needs to include Stream to modify the other file, although I have no idea how (retract(Stream, ...) doesn't work). Is there some I would be able to get the retract and assert predicates to function in the other file?
In SWI-Prolog you can assert/retract facts from a file that is used as a persistent fact store by using library persistency:
You declare fruit/3 as persistent. Optionally: you annotate the arguments with a type for automatic type checking.
You attach a file that will serve as the persistent fact store upon initialization of the fruit module (in this case fruit1.pl).
You add predicates for inserting (i.e., add_fruit/3) and querying (i.e., current_fruit/3) fruity facts. Retraction is handled similarly.
Notice that you can use the fact store in a multi-threaded environment by using with_mutex/2 (especially useful when you start retracting facts as well).
Code
:- module(
fruit,
[
add_fruit/3, % +Name:atom, +Price:float, +Color:atom
current_fruit/3 % ?Name:atom, ?Price:float, ?Color:atom
]
).
:- use_module(library(persistency)).
:- persistent(fruit(name:atom, price:float, color:atom)).
:- initialization(db_attach('fruit1.pl', [])).
add_fruit(Name, Price, Color):-
with_mutex(fruit_db, assert_fruit(Name, Price, Color)).
current_fruit(Name, Price, Color):-
with_mutex(fruit_db, fruit(Name, Price, Color)).
Illustration of use
Start Prolog, load fruit.pl, execute:
?- add_fruit(apple, 1.10, red).
Close Prolog, start Prolog (again), execute:
?- current_fruit(X, Y, Z).
X = apple,
Y = 1.1,
Z = red
You are now reading facts from fruit1.pl!
Illustration of automatic type checking
As mentioned before, the library also performs type checking for you, e.g.:
?- add_fruit(pear, expensive, green).
ERROR: Type error: `float' expected, found `expensive' (an atom)

Prolog error in loop

I would need help about Prolog.
I posted my code, the problem is that i do not obtain the expected result.
I want planning actions for moving on table all blocks until is possible. To do this I prompt :
?- do(while(some(x, block(x) & -onTable(x)),pi(x,putOnTable(x))),s0,S).
I expect to see a response like :
S = do(putOnTable(e), do(putOnTable(b), do(putOnTable(c), s0)))
but Prolog returns "false" only. Someone can help me??
% Golog interpreter
%:- [golog_swi].
:- discontiguous clear/2, on/3, onTable/2.
:- op(800,xfy,[&]).
do(E,S,do(E,S)):- primitive_action(E),poss(a,S).
% Primitive Action Declarations.
primitive_action(putOn(_,_)).
primitive_action(putOnTable(_)).
poss(putOn(X,Y),S) :- clear(X,S), clear(Y,S), \+ on(X,Y,S), \+ X=Y.
poss(putOnTable(X),S):- clear(X,S), \+(onTable(X,S)).
% Successor State Axioms.
on(X,Y,do(A,S)):- A = putOn(X,Y); on(X,Y,S), \+ (A = putOnTable(X); A = putOn(X,_)).
onTable(X,do(A,S)) :- A = putOnTable(X); onTable(X,S), \+ A= putOn(X,_).
clear(X,do(A,S)) :- on(Y,X,S), (A = putOn(Y,_) ; A = putOnTable(Y)); clear(X,S), \+ A = putOn(_,X).
% Restore suppressed situation arguments
restoreSitArg(onTable(X),S,onTable(X,S)).
restoreSitArg(on(X,Y),S,on(X,Y,S)).
restoreSitArg(clear(X),S,clear(X,S)).
block(X):- member(X,[a,b,c,d,e]).
% iniTial COndition
onTable(a,s0).
on(b,a,s0).
on(c,b,s0).
clear(c,s0).
onTable(d,s0).
on(e,d,s0).
clear(3,s0).
thank you!!!
Your predicate do/3 cannot succeed because the goal primitive_action/1 will fail with your query.
Currently, while/2 is not described in primitive_action/1 and it seems it is missing also from your program. So you need to extend primitive_action/1 by further facts, or add a new rule to do/3. And in addition to that you need to describe what while/2 means.
This question is actually about Golog. Your mistake is pretty mundane: you didn't copy the Golog interpreter code into your source file/directory.
Golog defines a number of high-level programming constructs, including while-loops and non-deterministic picks (pi), used here. I'm sure you don't want to reinvent Golog, so just go and get it. I'm assuming that your question is part of an assignment of sorts, and your teacher probably pointed you to the Golog interpreter. Otherwise, you can always find it on the pages of the cognitive robotics group at the Univ. of Toronto: http://www.cs.toronto.edu/cogrobo/main/systems/index.html

Resources