CS 396
|
Logic Programming Challenge
|
Points: 60 pts |
![]() |
Rules: Individual Effort. You may specifically NOT use solutions found on the internet, texts or elsewhere; such plagiarism will be severely santioned. |
In this exercise, we will explore the logic programming paradigm. We'll start with a little bit of light "programming" in Prolog to get the idea.
Before we try to implement any portion of a logic programming system, we need to get familiar with what "logic programming" is all about. The best way to do this is to do a couple of little toy problems using a real Prolog interpreter. We will be using SWI-Prolog, so you'll want to install it on your favorite machine. Visit the SWI prolog download page; looks like the latest stable release is currently 7.2, though for if you have the lastest MacOS, you'll need to get dev version 7.3. Make sure you read through the installation notes that come with it. In particular, it usually installs in /opt/local/bin or some similar directory; you'll probably want to add this to your local PATH to simplify using it.
Either way, you edit with your favorite text editor, then load (called "consult" in prolog) the source into the interpreter for testing. Edit and repeat as necessary. For details, here are some resources:
Part 1: Doing simple programming in Prolog.
Programming is strange in prolog: you have to phrase your desires as a predicate; "proving" that the predicate is true amounts to showing you an answer to the function call. So basically thinks of a predicate as "Is it true that the answer to this question with these arguments is X", where X is a variable. Prolog then does the proof...if it was able to prove it, it answers "Yes...with the variable X bound to this value". We'll just do a couple of these to give you the idea.
Just have to support two little predicates, about five lines of code total..
A simple math function called "multn"; just multiplies together the two positive integer args. The format is:
multn(int1,int2,result)
This is a very simple function just to give you some practice with the very basic ideas of prolog. To make this interesting, you must solve this problem in recursive fashion, and may NOT use the multiplication operation itself in the solution! So I'll be looking for a base case plus one other predicate. You may assume that only non-negative integer args are passed in. Some examples:
?- multn(5,3,OUT).
OUT = 15
?- multn(20,56,OUT).
OUT = 1120
A character deleter predicate called "ndelete";deletes regularly-spaced elements from a list.. The format is: ndelete(n, [inlist],[resultlist])
So this predicate deletes every nth element from the input list. As this is a predicate that you want to prove, the first two args are essentially the input arguments, and the third is the output argument. Some examples:
4 ?- ndelete(2,[a,b,c,d,e,f,g,h,y,t,r],OUT). OUT = [a, c, e, g, y, r] . 5 ?- ndelete(1,[a,b,c,d,f],OUT). OUT = [] . 6 ?- ndelete(4,[a,b,c,d,e,f,g,h,i],OUT). OUT = [a, b, c, e, f, g, i] .
Ok, now that we have gotten our feet wet with how Prolog work in general, let's just use the power of this language to solve a little problem for us:
Imagine that one day, a solar flare takes out all the cell towers (gasp!) and suddenly there are no cell phones, no texting and no internet! Oh the horror! The problem is that Dr. D desperately needs to reach Rachel, a student, to let her know that she's won the $1M Brilliance Award...but that she needs to show up to claim it by 5pm that day, or it goes to the runner-up.
So Dr. D sees Doug in the hallway and asks him to let Rachel know to come by ASAP. Doug says that he'll try to get the message to Rachel somehow. In particular, assume the following social network of connections between students:
So, what we want to write here is essentially a social network explorer to answer the burning question: does Rachel get the message and take home the big bucks? Plus any other "who talks to who problem we might throw at it!
Specifically, write a Prolog program that tackles this problem for you. For any two people A and B, it should output a path that the message could get from A to B; by hitting the ' ; ' repeatedly, you should then be able to get it to show you --- one after the other --- all possible paths that the message could get through by. In particular, your solution should provide the following top level functor:
connect(A, B) -- This functor takes in two arguments and tries to find (think: prove) a path between A and B.
in addition, all of the above facts must be represented by the following functor:
link(Person, Person) -- This means that the fact that "Doug talks to Luther" should become the fact link(doug,luther). You must use the link(a,b) functor to represent your facts; my grading program will count on it!
Important: You may use the "link" functor **only** as a fact in your code, and not as a rule statement. To test your code, my testing program starts by loading your code file, then *deleting* all link functors from the fact base to make sure we start clean. Then it loads my own link statements (my own social network) and tests your code with it. Therefore, you should only use link for the facts in your program...if I wipe them all out and load my own, nothing bad will happen. So you should have nothing that looks like "link(A,B) :- something" in your code!
I'll give you a hint on this, which also solves a problem that you need to solve anyway. Just include the following rule in your program:
knows(A,B) :- link(A,B); link(B,A).
So this says I can prove that A knows (talks to, is directly connected to) B if you have either a link(A,B) or a link(B,A) fact in your DB. In other words, it doesn't matter who is linked to who (the order of the people in the link), those people know each other....the connection is bi-directional. And now, in the rest of your code to solve the problem, you just use the knows functor.
So, with the fact base given above, I should be able to do:
27 ?- connect(dillon, colter). [dillon,colter] true ; [dillon,luther,doug,kyle,zach,charles,colter] true ; [dillon,luther,doug,kyle,colter] true ; [dillon,luther,doug,kyle,garrett,charles,colter] true ; [dillon,brandon,colter] true ; false.See what I mean? I asked it to prove a connection between Dillon and Colter. And it immediately found one: there is a simple fact that Dillon talks directly to Colter. Okay, but then I hit the ';' and it restarted the proof and showed me the next possible connection between the two. It kept showing me connections as I hit the ';' repeatedly...until it had exhausted all options and returned false.
Now use your program to answer the following questions:
Note that the *order* in which your program produces the solutions (as you hit semi-colon) doesn't matter...so long as it finds all possible paths.
Tips for tackling this challenge. Because Prolog is doing all the real work for you, the entire meat of the solution for this part is basically just TWO LINES LONG!! Really, no kidding: there I have another two little helpers to make it nice, but just one functor (with a base case and a recursive case) does the real work. That's the power of logic/declarative programming! Wow! Note I do have a bunch of facts, of course, reflecting who met who, which I'm not counting here...I'm just talking about the inference rules. The trick is to just see this as a network traversal problem: people are the "nodes" and whether or not they meet is an "arc" between those nodes. Given that, you're program just has to "explore" its way through this network, exploring all possible ways from the starting point A to the finish B. Or, to put it in Prolog terms, proving that a path between A and B exists.
Ok, so your "facts" are all in the form of "talks(personA, personB)". That's easy. Then you have the one key functor, nice and recursive, which has to express:
Base case: A communication targeted at person T -- and which is currently at some person N, having traversed some previous path P -- is proven if N is the same as T. Then I'm done! And the answer I return is just: the current person N stuck onto the front of the previous chain of people P.
Recursive case: communication currently at some person N, having followed a chain of people P so far, and targeted at some person T, could also succeed if it can be shown that person N met person N1, and if (recursion!) a connection from N1 to T can be shown to succeed.
So wow, I've just about given it all away! All you have to do is turn that into Prolog rules! A few pointers: