1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3% FILE : Eval/evalbat.pl 4% 5% Basic Action Projector (BAT) 6% 7% AUTHOR : Sebastian Sardina (prev. Hector Levesque & Maurice Pagnucco) 8% EMAIL : ssardina@cs.toronto.edu 9% WWW : www.cs.toronto.edu/~ssardina www.cs.toronto.edu/cogrobo 10% TYPE : system independent code 11% TESTED : SWI Prolog 5.0.10 http://www.swi-prolog.org 12% ECLIPSE 5.4 http://www.icparc.ic.ac.uk/eclipse/ 13% 14% This file allows for the projection of conditions wrt 15% basic action theories. 16% 17% The main tool provided in this file is the following predicate: 18% 19% -- eval(P,H,B): B=true/false/unknown is the truth value of P at history H 20% 21% For more information on Golog and some of its variants, see: 22% http://www.cs.toronto.edu/~cogrobo/ 23% 24%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 25% 26% March, 2002 27% 28% This software was developed by the Cognitive Robotics Group under the 29% direction of Hector Levesque and Ray Reiter. 30% 31% Do not distribute without permission. 32% Include this notice in any copy made. 33% 34% 35% Copyright (c) 2000 by The University of Toronto, 36% Toronto, Ontario, Canada. 37% 38% All Rights Reserved 39% 40% Permission to use, copy, and modify, this software and its 41% documentation for non-commercial research purpose is hereby granted 42% without fee, provided that the above copyright notice appears in all 43% copies and that both the copyright notice and this permission notice 44% appear in supporting documentation, and that the name of The University 45% of Toronto not be used in advertising or publicity pertaining to 46% distribution of the software without specific, written prior 47% permission. The University of Toronto makes no representations about 48% the suitability of this software for any purpose. It is provided "as 49% is" without express or implied warranty. 50% 51% THE UNIVERSITY OF TORONTO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 52% SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 53% FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF TORONTO BE LIABLE FOR ANY 54% SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 55% RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF 56% CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 57% CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 58% 59%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 60% 61% This file provides the following: 62% 63% -- eval(P, H, B) (MAIN PREDICATE, used by the transition system) 64% B is the truth value of P at history H 65% 66% SYSTEM TOOLS (used by the top-level cycle):: 67% 68% -- initializeDB/0 69% initialize the projector 70% -- finalizeDB/0 71% finalize the projector 72% -- can_roll(+H1) 73% check if the DB CAN roll forward 74% -- must_roll(+H1) 75% check if the DB MUST roll forward 76% -- roll_db(+H1,-H2) 77% perform roll forward with current history H1 and new history H2 78% -- handle_sensing(+A, +H, +S, -H2) 79% H2 is H plus action A with sensing result S 80% -- debug(+A, +H, -S) 81% perform debug tasks with current action A, sensing outcome S, 82% and history H 83% -- system_action(+A) 84% action A is an action used by the system (e.g., action outcomes e(_,_)) 85% 86% 87% OTHER TOOLS (used by the transition system):: 88% 89% -- sensing(+A, -L) 90% action A is a sensing action with a list L of possible outcomes 91% -- sensed(+A, +S, +H) 92% action A, when executed at history H, got sensing result S 93% -- inconsistent(+H) 94% last action turned history H inconsistent, i.e., impossible 95% -- domain(-V, +D) 96% object V is an element of domain D 97% -- rdomain(-V, +D) 98% object V is an element of domain D (random) 99% -- getdomain(+D, -L) 100% L is the list representing domain D 101% -- calc_arg(+A1, -A2, +H) 102% action A2 is action A1 with its arguments replaced wrt history H 103% -- before(+H1, +H2) 104% history H1 is a previous history of H2 105% -- assume(+F, +V, +H1, -H2) 106% H2 is the history resulting from assuming fluent F to 107% have value V at history H1 108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 109% 110% A basic action theory (BAT) is described with: 111% 112% -- fun_fluent(fluent) : for each functional fluent (non-ground) 113% (at least 1 clause is required) 114% -- rel_fluent(fluent) : for each relational fluent (non-ground) 115% (at least 1 clause is required) 116% -- cache(fluent) : fluent should be cached (at least 1) MANDATORY 117% 118% e.g., rel_fluent(painted(C)). 119% e.g., fun_fluent(color(C)). 120% 121% -- prim_action(action) : for each primitive action (ground) 122% -- exog_action(action) : for each exogenous action (ground) 123% 124% e.g., prim_action(clean(C)) :- domain(C,country). 125% e.g., exog_action(painte(C,B)):- domain(C,country), domain(B,color). 126% 127% -- senses(action,fluent) : for each sensing action that senses fluent directly 128% 129% e.g, senses(check_painted(C), painted(C)). 130% e.g, senses(senseTemperature, temp). 131% 132% -- senses(action,outcome,fluent,value,cond) : 133% for each sensing action that senses fluent *indirectly* 134% 135% e.g, senses(senseTemperature, T, temp, T, true). 136% : equivalent to senses(senseTemperature, temp). 137% e.g, senses(senseTemperature, T, temp, T2, T2 is T+10). 138% e.g, senses(senseTemperature, T, isHot, true, T>30). 139% e.g, senses(senseTemperature, T, isHot, false, T<=30). 140% 141% -- forget(action,fluent) : action makes fluent unknown 142% 143% e.g, poss(checkFloor, lightFloor). 144% -- poss(action,cond) : when cond, action is executable 145% 146% e.g, poss(clean(C), and(painted(C),holding(cleanear))). 147% 148% -- initially(fluent,value): fluent has value in S0 (ground) 149% (at least 1 clause is required) 150% 151% e.g., initially(painted(C), false):- domain(C,country), C\=3. 152% initially(painted(3), true). 153% initially(color(3), blue). 154% 155% -- causes_val(action,fluent,value,cond) 156% when cond holds, doing act causes functional fluent to have value 157% 158% e.g., causes_val(paint(C2,V), color(C), V, C = C2). 159% or causes_val(paint(C,V), color(C), V, true). 160% 161% -- causes_true(action,fluent,cond) 162% when cond holds, doing act causes relational fluent to hold 163% -- causes_false(action,fluent,cond) 164% when cond holds, doing act causes relational fluent to not hold 165% 166% e.g., causes_true(paint(C2,_), painted(C), C = C2). 167% or causes_true(paint(C,_), painted(C), true). 168% e.g., causes_false(clean(C2), painted(C), C = C2). 169% or causes_false(clean(C), painted(C), true). 170% 171% -- sort-name(domain_of_sort). : defines a sort 172% e.g., color([blue, green, yellow, red]). 173% temperature([-30..45]). 174% 175% 176% A high-level program-controller is described with: 177% 178% -- proc(name,P): for each procedure P 179% -- simulator(N,P): P is the N exogenous action simulator 180% 181% The interface for real-world execution is described with: 182% 183% -- actionNum(action, num) 184% action has RCX code num 185% -- simulateSensing(action) 186% sensing result for action should be asked to the user 187% -- translateSensing(action, sensorValue, sensorResult) 188% translate the sensorValue of action to sensorResult 189% -- translateExogAction(codeAction, action) 190% translateSensing action name into codeAction and vice-versa 191% 192% Requirements: 193% 194% -- is_list(+L) : L is a list 195% -- subv(X1,X2,T1,T2) : T2 is T1 with X1 replaced by X2 196% -- multifile/1 197% -- get0/1 198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 199%% :- module(evalbat, 200%% [eval/3, 201%% initializeDB/0, 202%% finalizeDB/0, 203%% handle_sensing/4, 204%% sensing/2, 205%% sensed/3, 206%% domain/2, 207%% getdomain/2, 208%% calc_arg/3, 209%% before/2, 210%% inconsistent/1, 211%% assume/4 212%% ]). 213%% 214%% :- use_module(library(quintus)). 215 216:- dynamic 217 currently/2, % Used to store the actual initial fluent values 218 simulator/2, % There may be no simulator 219 senses/2, 220 senses/5, % There may be no sensing action 221 forget/2, % There may be no action that "forgets" a fluent 222 has_valc/3. % used for caching some values 223 224% Predicates that they have definitions here but they can defined elsewhere 225:- multifile(prim_action/1). 226:- multifile(causes_val/4). 227%:- multifile(causes_true/3). 228%:- multifile(causes_false/3). 229%:- multifile(exog_action/1). 230:- multifile(poss/2). 231 232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 233% PREDICATES TO BE EXPORTED 234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 235 236 /* Move initially(-,-) to currently(-,-) and clear exog actions */ 237initializeDB:- 238 retractall(currently(_,_)), 239 initially(F,V), 240 assert(currently(F,V)), 241 clean_cache, 242 fail. 243initializeDB. 244 245 /* Clean all the currently(-.-) predicates */ 246%finalizeDB:- retractall(currently(_,_)), clean_cache. 247finalizeDB. 248 249% eval(P,H,B): this is the interface of the projector 250eval(P,H,true):- holds(P,H). 251 252% Change the history H to encode the sensing result of action A at H 253%handle_sensing(A,H,Sr,[e(F,Sr)|H]):- senses(A,F). (OLD WAY) 254handle_sensing(A,H,Sr,[e(A,Sr)|H]). 255 256 257% clean_cache: remove all has_valc/3 258clean_cache :- retractall(has_valc(_,_,_)). 259 260% Set F to value V at H, return H1 (add e(F,V) to history H) 261assume(F,V,H,[e(F,V)|H]). 262 263% system_action/1 defines actions that are used by the projector for managment 264system_action(e(_,_)). 265 266% Action A is a sensing action 267sensing(A,_):- senses(A,_) ; senses(A, _, _, _, _). 268 269% sensed(+A,?V,+H): action A got sensing result V w.r.t. history H 270sensed(A,V,[e(F,V2)|_]):- senses(A,F), !, V=V2. 271sensed(A,V,[_|H]) :- sensed(A,V,H). 272 273% domain/2: assigns a user-defined domain to a variable. 274domain(V, D) :- getdomain(D, L), member(V, L). 275rdomain(V, D) :- getdomain(D, L), shuffle(L,L2), !, member(V, L2). 276 277% L is the list-domain associated to name D 278getdomain(D, L) :- is_list(D) -> L=D ; (P =.. [D,L], call(P)). 279 280% Computes the arguments of an action or a fluent P 281% Action/Fluent P1 is action/fluent P with all arguments evaluated 282calc_arg(P,P1,H):- (is_an_action(P) ; prim_fluent(P)), 283 (atomic(P)-> P1=P ; 284 (P =..[Function|LArg], subfl(LArg,LArg2,H), 285 P1=..[Function|LArg2])). 286 287% History H1 is a previous history of H2 288before(H1,H2):- append(_,H1,H2). 289 290% No action can make a history inconsistent (simplification) 291inconsistent(_):- fail. 292 293 294 295 296%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 297% OTHER PREDICATES NEEDED BUT NOT EXPORTED 298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 299 300% A primitive fluent is either a relational or a functional fluent 301prim_fluent(P):- rel_fluent(P) ; fun_fluent(P). 302 303% Check if A has "the form" of a primitive action, though, its arguments 304% may need to be evaluated yet 305% We need to do this hack because actions are defined all ground 306is_an_action(A):- \+ \+ (prim_action(A) ; exog_action(A)), !. 307is_an_action(A):- \+ atomic(A), 308 A =..[F|Arg], length(Arg,LArg), length(ArgV,LArg), 309 NA =..[F|ArgV], (prim_action(NA) ; exog_action(A)). 310 311% Simulation of an action A has the same effects as action A itself 312causes_val(sim(A),F,V,C) :- !, causes_val(A,F,V,C). 313 314% Build causes_val/4 for relational fluents 315causes_val(A,F,true,C) :- causes_true(A,F,C). 316causes_val(A,F,false,C) :- causes_false(A,F,C). 317 318% Abort if P is not grounded (to use before negations as failure) 319checkgr(P):- ground(P)-> true ; once(warn(['CWA applied to formula: ',P])). 320 321 322% Update the cache information by stripping out the subhistory H 323update_cache(H) :- 324 retract(has_valc(F, V, H2)), 325 append(H1, H, H2), 326 assert(has_valc(F, V, H1)), 327 fail. 328update_cache(_). 329 330 331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 332% HOLDS - Here starts the evaluation procedure for projection 333%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 334% kwhether(F,H): fluent F is known true or false in H 335% Assumes that after sensing F, F may change but it will remain known 336% We may probably want to add some "forgeting" mechanism.. either by a 337% state condition or special actions 338holds(kwhether(F),[]) :- !, initially(F,_). 339holds(kwhether(F),[Act|_]):- (senses(Act,F) ; Act=e(F,_)), !. 340holds(kwhether(F),[_|H]) :- holds(kwhether(F),H). 341 342% know(F): Fluent F evaluates to something 343holds(know(F),H) :- !, holds(F=_,H). 344 345% holds(P,H): P holds in H 346holds(and(P1,P2),H) :- !, holds(P1,H), holds(P2,H). 347holds(or(P1,P2),H) :- !, (holds(P1,H) ; holds(P2,H)). 348holds(neg(P),H) :- !, checkgr(P), \+ holds(P,H). /* Negation by failure */ 349holds(some([],P),H) :- !, holds(P,H). 350holds(some([V|L],P),H) :- !, holds(some(V,some(L,P)), H). 351holds(some([],P),H) :- !, holds(P,H). 352holds(some([V|L],P),H) :- !, holds(some(V,some(L,P)), H). 353holds(some(V,P),H) :- !, subv(V,_,P,P1), holds(P1,H). 354holds(some((V,D),P),H) :- !, domain(O,D), subv(V,O,P,P1), holds(P1,H). 355holds(all([],P),H) :- !, holds(P, H). 356holds(all([V|L],P),H) :- !, holds(all(V,all(L,P)), H). 357holds(all((V,D),P),H) :- !, holds(neg(some(V,D),neg(P)), H). 358holds(impl(P1,P2),H) :- !, holds(or(neg(P1),P2),H). 359holds(P,H) :- proc(P,P1), !, (ground(P) -> (holds(P1,H), !) ; holds(P1,H)). 360holds(P,H) :- ground(P), rel_fluent(P), !, subf(P,true,H), !. 361holds(P,H) :- \+ \+ rel_fluent(P), !, rel_fluent(P), subf(P,true,H). 362holds(P,H) :- subf(P,P1,H), (ground(P) -> (call(P1), !) ; call(P1)). 363 364 /* P2 is P1 with all fluents replaced by their values at H */ 365subf(P1,P2,_) :- (var(P1) ; number(P1)), !, P2 = P1. 366subf(P1,P2,H) :- atom(P1), !, subf2(P1,P2,H). 367subf(P1,P2,H) :- P1=..[F|L1], subfl(L1,L2,H), P3=..[F|L2], subf2(P3,P2,H). 368 369subf2(P3,P2,H) :- prim_fluent(P3), has_value(P3,P2,H). 370subf2(P2,P2,_) :- \+ prim_fluent(P2). 371 372subfl([],[],_). 373subfl([T1|L1],[T2|L2],H) :- subf(T1,T2,H), subfl(L1,L2,H). 374 375 376% 377% has_value(F,V,H): Fluent F has value V at history H 378% 379has_value(F,V,H) :- ground(F) -> (has_valg(F,V,H), !) ; has_valo(F,V,H). 380 381% has_valg/3: check cache, then normal query (for ground queries) 382has_valg(F,V,H) :- cache(F), !, 383 (has_valc(F,V,H) -> true ; (has_val(F,V,H), assert(has_valc(F,V,H))) ). 384has_valg(F,V,H) :- has_val(F,V,H), !. % F is a fluent with NO cache 385 386% has_valo/3: check cache, then normal query (for ground queries) 387has_valo(F,V,H) :- cache(F), !, 388 (has_valc(F,V,H) ; (has_val(F,V,H), \+ has_valc(F,V,H), assert(has_valc(F,V,H)))). 389has_valo(F,V,H) :- has_val(F,V,H). % F is a fluent with NO cache 390 391 392% has_val/3: the usual way of reasoning using regression and sensing 393has_val(F,V,[]) :- currently(F,V). 394has_val(F,V,[A|H]) :- sets_val(A,F,V,H). 395has_val(F,V,[A|H]) :- \+ forget(A,H,F), has_value(F,V,H), \+ sets_val(A,F,_,H). 396 397sets_val(e(F,V),F,V,_) :- prim_fluent(F), !. % Fluent V is explicitly set by e(_,_) 398sets_val(e(A,V),F,V,_) :- senses(A,F). % Action A sets F directly 399sets_val(e(A,V),F,V2,H) :- !, senses(A,V,F,V2,P), holds(P,H). % A sets F indirectly 400sets_val(A,F,V,H) :- causes_val(A,F,V,P), holds(P,H). % Non-sensing reasoning 401 402% So far, one forgets the value of F when it is sensed (may be improved) 403forget(Act, _, F) :- forget(Act, F). 404 405% Special high-level actions to set and unset fluent F: set(F) and unset(F) 406prim_action(set(_)). 407prim_action(unset(_)). 408poss(set(F), ground(F)). 409poss(unset(F), ground(F)). 410has_val(F,V,[set(F)|_]) :- !, V=true. 411has_val(F,V,[unset(F)|_]):- !, V=false. 412 413 414 415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 416% ROLL DATABASE FORWARD 417% 418% Rolling forward means advancing the predicate currently(-,-) and 419% discarding the corresponding tail of the history. 420% There are 3 parameters specified by roll_parameters(L,N,M). 421% L: the history has to be longer than this, or dont bother 422% M: if the history is longer than this, forced roll 423% N: the length of the tail of the history to be preserved 424% (set N=0 to never roll forward) 425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 426:- dynamic temp/2. % Temporal predicate used for rolling forward 427 428%roll_parameters(1,1,0). % Never roll forward 429roll_parameters(20,40,5). 430 431can_roll(H) :- roll_parameters(L,_,N), length(H,L1), L1 > L, N>0. 432must_roll(H) :- roll_parameters(_,M,N), length(H,L1), L1 > M, N>0. 433 434% H1 is the current history (H1 = H2 + H3) 435% H2 will be the new history 436% H3 is the tail of H1 that is going to be dropped 437roll_db(H1,H2) :- 438 roll_parameters(_,_,N), 439 split(N,H1,H2,H3), 440 preserve(H3), 441 update_cache(H3). % Update the cache wrt the preserved history H3 442 443 /* split(N,H,H1,H2) succeeds if append(H1,H2,H) and length(H1)=N. */ 444split(0,H,[],H). 445split(N,[A|H],[A|H1],H2) :- N > 0, N1 is N-1, split(N1,H,H1,H2). 446 447% preserve(H) : rolls forward the initial database from [] to H 448preserve([]). 449preserve([A|H]) :- 450 preserve(H), 451 roll_action(A), 452 update_cache([A]). 453 454% roll_action(A): roll currently/2 database with respect to action A 455roll_action(A) :- 456 sets_val(A, F, V, []), 457 prim_fluent(F), 458 (\+ temp(F, V) -> assert(temp(F, V)) ; true), 459 fail. 460roll_action(_) :- 461 retract(temp(F,V)), 462 retractall(currently(F,_)), % There should be just one currently/2 for F! 463 assert(currently(F,V)), 464 fail. 465roll_action(_). 466 467 468 469 470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 471% DEBUG ROUTINES 472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 473% debug(+Action, +History, -SensingResult): 474% If Action=debug then a snapshot of the system is printed out 475% Otherwise, the sendRcxActionNumber/2 476% predicate failed (RCX panicked or there was a problem with the 477% communication). This predicate attempts to provide some basic debug 478% and error recovery. 479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 480debug(debug, History, _) :- !, 481 write('-------------------------------------------------------------'), nl, 482 write('********* A SNAPSHOT OF THE SYSTEM WAS REQUESTED ************'), nl, 483 errorRecoveryData(History), 484 write('-------------------------------------------------------------'), nl. 485 486debug(Action, History, SensingResult) :- 487 write('** There is a problem with the RCX. It may need to be reset.'), nl, 488 errorRecoveryData(History), 489 errorRecoveryProc, 490 execute(Action, History, SensingResult). % Try action again 491 492% errorRecoveryData(+History): Extract values of primitive fluents at 493% the point where Hist actions are performed. 494errorRecoveryData(History) :- 495 write(' Actions performed so far: '), 496 write(History), nl, 497 bagof(U, prim_fluent(U), FluentList), 498 printFluentValues(FluentList, History). 499 500% printFluentValues(+FluentList, +History): Print value of primitive fluents 501% at the point where History actions have been performed 502printFluentValues([], _). 503 504printFluentValues([Hf | FluentList], History) :- 505 (has_value(Hf, Hv, History), % Print all instances of Hf 506 write(' PRIMITIVE FLUENT '), 507 write(Hf), 508 write(' HAS VALUE '), 509 write(Hv), nl, fail) ; 510 printFluentValues(FluentList, History). % Continue with other fluents 511 512% errorRecoveryProc: What to do in case of error. In this case, ask the user 513% to reposition the RCX so that last action can be re-attempted 514errorRecoveryProc:- 515 write('If you wish to abort, enter "a".'), nl, 516 write('If you wish to continue execution, place RCX in a position'), nl, 517 write('consistent with these values and hit any other key.'), nl, 518 get0(Val), 519 get0(_), % Clear carriage return 520 (Val == 65; Val == 97) -> % 65 is ASCII 'A', 97 is ASCII 'a' 521 abort; 522 true. 523 524 525%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 526% EOF: Eval/evalbat.pl 527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%