---+++ LPS in Prolog This version of *LPS* is implemented in the programming language Prolog, and exploits the SWISH implementation of SWI Prolog. Much of the LPS syntax and some of the other features of LPS are inherited from Prolog. Consider the following, more elaborate variant of the light switch example:
:- expects_dialect(lps). maxTime(5). fluents light(_,_), location(_,_). actions switch(_,_, _), goto(_,_). initially light(livingroom, off), light(kitchen, on), light(bedroom, on), light(bathroom, on), location(bob, livingroom), location(dad, kitchen). opposite(off, on). opposite(on, off). if location(bob, Place) at T1, light(Place, off) at T1 then switch(bob, Place, on) from T1 to T2. if light(Place, on) at T1, location(dad, Place) at T1 then switch(dad, Place, off) from T1 to T2. if light(Place, on) at T1, not location(dad, Place) at T1 then goto(dad, Place) from T2 to T3. switch(Person, Place, New) initiates light(Place, New). switch(Person, Place, New) terminates light(Place, Old). goto(Person, Place) initiates location(Person, Place). goto(Person, _) terminates location(Person, _).
Notice that in this example, the three clauses: switch(Person, Place, New) initiates light(Place, New). opposite(off, on). opposite(on, off). replace the two clauses in the earlier notebook: switch initiates lightOn if lightOff. switch initiates lightOff if lightOn. The two facts defining the predicate "opposite" are actually written and executed in Prolog. In general, clauses that do not depend upon time in LPS are written in Prolog. Before running the example, be aware that (as in Prolog) all occurrences of comma (",") mean "and". Also, all sentences (ending in a full stop (".") are also connected by "and". Notice too that predicate symbols and constants all begin with a lower case letter. Variables begin with an upper case letter or with an underscore "_". Underscore is used to represent a variable whose name doesn't matter (and is therefore "anonymous") because it does not occur elsewhere in the same sentence. Underscore is useful because singleton variables are often a source of error in Prolog. In the program above the singleton variable *Person* in lines 21 and 23 is not erroneous, but is highlighted in bold font, to draw attention to the possibility of an error. Now try running the program.
go(Timeline).
Are you surprised? Who would have thought that dad could do so many things and be in so many places at the same time? Fortunately, we can constrain such unrealistic or unwanted behaviour by means of constraints on actions. ---++++ Constraints on Actions We can prevent dad being in two places at once, by constraining him (and anyone else, for that matter) from going to two different places at the same time. We can also prevent him from activating a switch and going to a place at the same time. Such unrealistic behaviour can be prevented by constraints, which have the general form: false conditions. where conditions is a list of conditions, separated by commas. At least one condition must be an action. All actions in the constraint are assumed to take place at the same time, say from T to T+1, and all fluents are assumed to hold at the same time T. In this example, we need to add two constraints: false goto(Person, Place1), goto(Person, Place2), Place1 \= Place2. false goto(Person, _), switch(Person, _, _). Let's also increase the length of time:
maxTime(10). fluents light(_,_), location(_,_). actions switch(_,_, _), goto(_,_). initially light(livingroom, off), light(kitchen, on), light(bedroom, on), light(bathroom, on), location(bob, livingroom), location(dad, kitchen). opposite(off, on). opposite(on, off). if location(bob, Place) at T1, light(Place, off) at T1 then switch(bob, Place, on) from T1 to T2. if light(Place, on) at T1, location(dad, Place) at T1 then switch(dad, Place, off) from T1 to T2. if light(Place, on) at T1, not location(dad, Place) at T1 then goto(dad, Place) from T2 to T3. switch(Person, Place, New) initiates light(Place, New). % if light(Place, Old), opposite(New, Old). switch(Person, Place, New) terminates light(Place, Old). % if opposite(New, Old). goto(Person, Place) initiates location(Person, Place). goto(Person, _) terminates location(Person, _). false goto(dad, Place1), goto(dad, Place2), Place1 \= Place2. false goto(Person,_), switch(Person, _, _).
go(Timeline).
This is, no doubt, much better. But why does dad keep going back to the bedroom and bathroom, even after he has already swiched off their lights? It's because the rules say that for every time T1 that the light is on in a room, there must be a future time T2 at which dad goes to the room. If you change maxTime to, say, 30, you will see that eventually dad will have gone to a room for every time the light is on in the room. Then, starting at time 19, dad will settle down to staying in the living room, switching the light off, whenever bob switches the light on. LPS is not clever enough to realise that one action of going to a room is good enough to satisfy the rule for all the consecutive times the light is on in the room. However, you can add an extra constraint, to prevent dad from going to a room when the light is already off. Try it!