madagascar/heuristics2.c

829 lines
21 KiB
C

/* 2012 (C) Jussi Rintanen */
#define noDECDEBUG
#define noHEURDEBUG
#define noDEBUGDISJ
typedef struct {
int l; /* Will satisfy subgoal l */
int var; /* with variable var (action, conditional effect) */
short t; /* at time point t */
int flaws; /* How many later actions this action disables/affects */
} planstep;
#define MAXPLANSTEPS 1000000
planstep plansteps[MAXPLANSTEPS];
int Nsteps;
#define STACKSIZE 50000
typedef struct _stackel {
int lit;
short t;
short val;
} stackel;
int stackptr;
stackel stack[STACKSIZE];
/* Order the subgoals according to a measure.
0. No measure is used (everything is 0).
1. How early does it have to be true.
TODO: 2. How early, + how constrained.
If two are equally early, choose the one that is more constrained
in the sense that can be TRUE in a smaller number of consecutive
time points. Choosing a highly constrained subgoal leads to failures
sooner.
*/
inline int anyactionapplicable(satinstance sati,int l,int t) {
compactCEstruct *ptr;
for(ptr=cCEs[l];ptr->var != -1;ptr++) { /* All ways of making l true. */
if(!tvarfalsep(sati,ptr->var,t)) { /* Is it applicable? */
return 1;
}
}
return 0;
}
inline int goallitvalue(satinstance sati,int l,int t0) {
int t,t2;
t = t0;
switch(HEURordmode) {
case 1:
/* How long the goal lit has been true before t. */
while(t >= 0 && tlittruep(sati,l,t)) t = t - 1;
return t+1;
case 2:
/* How long the goal lit has been true before t. */
while(t >= 0 && tlittruep(sati,l,t)) t = t - 1;
t2 = t;
/* How much earlier it was last false. */
while(t2 >= 0 && !tlitfalsep(sati,l,t2)) t2 = t2-1;
return (t+1)*200+(t-t2);
case 3:
/* How long the goal lit must have been true before t (check applicability
of actions making it true!) */
t = t-1;
while(t >= 0 && anyactionapplicable(sati,l,t) == 0) t = t - 1;
return t+1;
case 4:
/* How wide is the gap in which it must be made true? */
/* WARNING: This stops depth-first! */
t = t-1;
while(t >= 0 && anyactionapplicable(sati,l,t) == 0) t = t - 1;
t2 = t;
while(t2 >= 0 && !tlitfalsep(sati,l,t2)) t2 = t2 - 1;
return t-t2;
case 5:
/* Case 1 with Case 4 as a tie-breaker. */
/* How wide is the gap in which it must be made true? */
t = t-1;
while(t >= 0 && tlittruep(sati,l,t)) t = t - 1;
t2 = t;
while(t2 >= 0 && !tlitfalsep(sati,l,t2)) t2 = t2 - 1;
return t*1000+t-t2;
case 6:
/* Case 3 with Case 4 as a tie-breaker. */
/* How wide is the gap in which it must be made true? */
t = t-1;
while(t >= 0 && anyactionapplicable(sati,l,t) == 0) t = t - 1;
t2 = t;
while(t2 >= 0 && !tlitfalsep(sati,l,t2)) t2 = t2 - 1;
return t*1000+t-t2;
default:
return 0;
}
}
void push2goalstack(satinstance sati,int l,int t) {
int v,i;
i = stackptr++;
ASSERT(stackptr < STACKSIZE);
v = goallitvalue(sati,l,t);
/* Insert the literal in the stack. Lowest .val is on top (last). */
/* This means that we have a depth-first traversal: support for
one precondition (or top-level goal) is found first, before
proceeding with another precondition (or top-level goal). */
while(i > 0 && stack[i-1].val < v) {
stack[i].lit = stack[i-1].lit;
stack[i].t = stack[i-1].t;
stack[i].val = stack[i-1].val;
i = i-1;
}
stack[i].lit = l;
stack[i].t = t;
stack[i].val = v;
}
/* Push the constituent literals of a conjunctive goal formula in the stack. */
void push2goalstackCfma(satinstance sati,fma *f,int t) {
fmalist *fs;
switch(f->t) {
case patom: push2goalstack(sati,PLIT(f->a),t); break;
case natom: push2goalstack(sati,NLIT(f->a),t); break;
case conj:
fs = f->juncts;
while(fs != NULL) {
push2goalstackCfma(sati,fs->hd,t);
fs = fs->tl;
}
break;
case TRUE: break;
default:
assert(1==2);
}
}
/* Push a subset of the literals in a general NNF formula which are
sufficient for making the formula true, in the stack.
This is as in the ICAPS'11 submission.
For conjunctions all conjuncts have to be taken.
For disjunctions one of the disjuncts is taken. If at least one of
the disjuncts is true or unknown, then the chosen disjunct cannot
be a false one.
*/
/* The implementation is in with a stack of sets of literals.
The operations are
- adding a set into the set (empty or singleton)
- taking the union of two top stacks
- removing the top or the second set from the stack
- comparing the two top sets (cardinality?) for choosing a disjunct
tset_stack: element is the index of the first element of the set in tset_store
*/
typedef struct {
int l;
short t;
} storepair;
int stacktop;
int storetop;
int tset_stack[1000];
int tset_card[1000];
storepair tset_store[100000];
#define CARDTOP (storetop-tset_stack[stacktop]+1)
#define CARD2ND (tset_stack[stacktop]-tset_stack[stacktop-1])
void tset_emptyset() {
#ifdef DEBUGDISJ
printf("tset_emptyset\n");
#endif
stacktop += 1;
tset_card[stacktop] = 0;
tset_stack[stacktop] = storetop+1;
}
void tset_show() {
#ifdef DEBUGDISJ
int i,j;
printf("=========================================================\n");
for(i=0;i<=storetop+1;i++) {
for(j=0;j<=stacktop;j++) {
if(tset_stack[j] == i) printf("START %i (card %i)\n",j,tset_card[j]);
}
if(i<=storetop) {
printf("%i: ",i); printlit(tset_store[i].l); printf("@%i\n",tset_store[i].t);
}
}
printf(" =========================================================\n");
#endif
}
/* Add the empty set in the stack. */
void tset_makeempty() {
#ifdef DEBUGDISJ
printf("tset_makeempty\n");
#endif
stacktop=-1;
storetop=-1;
tset_emptyset(); /* Always have an empty set in the stack. */
}
/* Add a singleton set into the stack. */
void tset_singleton(int l,int t) {
#ifdef DEBUGDISJ
printf("tset_singleton "); printlit(l); printf("@%i\n",t);
#endif
storetop = storetop+1;
tset_store[storetop].l = l;
tset_store[storetop].t = t;
stacktop = stacktop+1;
tset_stack[stacktop] = storetop;
tset_card[stacktop] = 1;
}
/* Take the union of the two top sets. */
void tset_union() {
#ifdef DEBUGDISJ
printf("tset_union\n");
#endif
tset_card[stacktop-1] = tset_card[stacktop-1]+tset_card[stacktop];
stacktop = stacktop - 1;
}
/* Return TRUE if top element has a smaller cardinality than the second. */
int tset_top_better() {
if(tset_card[stacktop] < tset_card[stacktop-1]) return 1; else return 0;
}
/* Remove the top set from the stack. */
void tset_top_delete() {
#ifdef DEBUGDISJ
printf("tset_top_delete\n");
#endif
storetop = storetop-tset_card[stacktop];
stacktop = stacktop-1;
}
/* Remove the second set from the stack. */
void tset_second_delete() {
int i,n;
#ifdef DEBUGDISJ
printf("tset_second_delete\n");
#endif
n = tset_card[stacktop];
/* Move the top set where the second one was. */
for(i=0;i<n;i++) {
tset_store[tset_stack[stacktop-1]+i] = tset_store[tset_stack[stacktop]+i];
}
storetop = tset_stack[stacktop-1]+n-1;
stacktop = stacktop-1;
tset_card[stacktop]=n;
}
int traverseDfma(satinstance sati,fma *f,int t) {
fmalist *fs;
int have;
#ifdef DEBUGDISJ
tset_show();
#endif
switch(f->t) {
/* Here we have to restrict to literals that are not FALSE ? */
case patom:
if(tvarfalsep(sati,f->a,t)) return 0;
tset_singleton(PLIT(f->a),t);
return 1;
case natom:
if(tvartruep(sati,f->a,t)) return 0;
tset_singleton(NLIT(f->a),t);
return 1;
case conj: /* The literal sets for all conjuncts are combined. */
tset_emptyset();
fs = f->juncts;
while(fs != NULL) {
if(traverseDfma(sati,fs->hd,t) == 0) {
tset_top_delete();
return 0;
}
tset_union();
fs = fs->tl;
}
return 1;
case disj: /* The set for one of the disjuncts is chosen, others ignored. */
// tset_emptyset(); ???????????????????
have = 0;
fs = f->juncts;
while(fs != NULL) {
if(traverseDfma(sati,fs->hd,t)) {
if(have) {
#ifdef DEBUGDISJ
tset_show();
#endif
if(tset_top_better()) tset_second_delete(); else tset_top_delete();
}
have = 1;
}
fs = fs->tl;
}
return have;
case TRUE: tset_emptyset(); return 1;
case FALSE: return 0;
}
return 0;
}
void push2goalstackDfma(satinstance sati,fma *f,int t) {
int i,l,t2;
tset_makeempty();
if(traverseDfma(sati,f,t) == 0) return;
#ifdef DEBUGDISJ
tset_show();
#endif
// if(tset_stack[stacktop]+1>storetop) printf("Not pushing anything\n");
/* Push the literals from the traverseDfma stack into the heuristic stack. */
for(i=tset_stack[stacktop];i<=storetop;i++) {
l = tset_store[i].l;
t2 = tset_store[i].t;
#ifdef DEBUGDISJ
printf("Pushing %i:",l); printlit(l); printf("@%i\n",t2);
#endif
push2goalstack(sati,l,t2);
}
}
#ifdef HARDDEBUG
void showheustack(satinstance sati) {
int i;
printf("STACK CONTENTS:\n");
for(i=0;i<stackptr;i++) {
printlit(stack[i].lit);
printf("@%i (score %i)\n",stack[i].t,stack[i].val);
}
}
#endif
#define TMPARRAYSIZE 1000
void do_cpath(satinstance);
int weightedscore(satinstance sati,int i) {
return sati->lits[PLIT(TVAR(plansteps[i].var,plansteps[i].t))].score + 10000-100*plansteps[i].flaws;
}
/* Identify actions that are useful in reaching the goals. */
int do_cpath_heuristic(satinstance sati) {
int i,j,best,besttime;
#ifdef WEIGHTS
int tmparray[TMPARRAYSIZE];
#endif
nextround(sati);
Nsteps = 0;
stackptr = 0;
switch(sati->heuristic_mode) {
case 0: /* Choose an action. */
if(goalisdisjunctive) {
push2goalstackDfma(sati,goal,sati->nOfTPoints-1);
} else {
push2goalstackCfma(sati,goal,sati->nOfTPoints-1);
}
#ifdef HARDDEBUG
showheustack(sati);
#endif
/* Find paths */
do_cpath(sati);
/* Pick action. */
/* Only one action to choose from: return it directly. */
if(Nsteps == 1) return PLIT(TVAR(plansteps[0].var,plansteps[0].t));
if(Nsteps > 0) {
switch(HEURactionchoice) {
case 0: /* Choose action randomly. */
i = random() % Nsteps;
break;
case 1: /* Choose the earliest possible action. */
best = -1;
besttime = 100000;
for(i=0;i<Nsteps;i++) {
if(plansteps[i].t < besttime) {
best = i;
besttime = plansteps[i].t;
}
}
i = best;
break;
case 2: /* Choose action randomly, with bias to earlier. */
j = random() % Nsteps;
i = random() % (j+1);
break;
case 3: /* Choose action randomly, with bias to later. */
j = random() % Nsteps;
i = Nsteps-(random() % (j+1))-1;
break;
case 4: /* Choose action based on its weight, with ties broken
randomly. */
#ifdef WEIGHTS
best = -1;
for(i=0;i< Nsteps;i++) { /* Determine the best score. */
int score = weightedscore(sati,i);
if(score > best) best = score;
}
j = 0;
for(i=0;i<Nsteps;i++) { /* Collect the literals with the best score. */
if(weightedscore(sati,i) == best) {
tmparray[j++] = i;
ASSERT(j < 1000);
}
}
i = tmparray[random() % j];
break;
#else
fprintf(stderr,"Heuristic needs WEIGHTS compiled.\n");
exit(1);
#endif
}
return PLIT(TVAR(plansteps[i].var,plansteps[i].t));
// return TLIT(plansteps[i].l,plansteps[i].t);
}
sati->heuristic_mode = 1;
sati->heuristic_time = 1;
sati->heuristic_index = 0;
case 1: /* No actions needed. Do inertia. */
#ifdef HEURDEBUG
printf("Doing INERTIA\n");
#endif
while(sati->heuristic_time < sati->nOfTPoints) {
i = sati->heuristic_index;
j = sati->heuristic_time;
if(i+1 == sati->nOfSVars) {
sati->heuristic_index = 0;
sati->heuristic_time += 1;
} else {
sati->heuristic_index += 1;
}
if(tvarunsetp(sati,i,j)) {
if(tvartruep(sati,i,j-1)) return PLIT(TVAR(i,j));
else return NLIT(TVAR(i,j));
}
}
sati->heuristic_mode = 2;
sati->heuristic_time = 0;
sati->heuristic_index = 0;
case 2: /* All state variables have a value. Set actions to FALSE. */
while(sati->heuristic_time < sati->nOfTPoints-1) {
i = sati->heuristic_index;
j = sati->heuristic_time;
if(i+1 == sati->nOfActions) {
sati->heuristic_index = 0;
sati->heuristic_time += 1;
} else {
sati->heuristic_index += 1;
}
if(tvarunsetp(sati,ACTVAR(i),j)) return NLIT(TVAR(ACTVAR(i),j));
}
}
return -1;
}
int fmavalue(satinstance sati,fma *f,int t) {
fmalist *juncts;
switch(f->t) {
case patom: if(!tvarfalsep(sati,f->a,t)) return 1; else return 0;
case natom: if(!tvartruep(sati,f->a,t)) return 1; else return 0;
case disj:
juncts = f->juncts;
while(juncts != NULL) {
if(fmavalue(sati,juncts->hd,t) != 0) return 1;
juncts = juncts->tl;
}
return 0;
case conj:
juncts = f->juncts;
while(juncts != NULL) {
if(fmavalue(sati,juncts->hd,t) == 0) return 0;
juncts = juncts->tl;
}
return 1;
case TRUE: return 1;
case FALSE: return 0;
}
return 0;
}
/* Estimate the value for an action */
int actionvalue(satinstance sati,int var,int t,fma *precondition,int disjunctive) {
int i;
int val;
switch(HEURops) {
case 1: /* Evaluate 'constrainedness' of action */
/* How many steps later can this action be taken? */
val = 0;
while(t+1 < sati->nOfTPoints-1 && !tvarfalsep(sati,var,t+1)) {
t = t+1;
val = val + 1;
}
return val;
case 2: /* Evaluate 'constrainedness' of action */
/* How many steps later can this action be taken? */
val = 0;
while(t+1 < sati->nOfTPoints-1 && !tvarfalsep(sati,var,t+1)) {
t = t+1;
val = val + 1;
}
return 1000-val;
case 3: /* Evaluate 'constrainedness' of precondition */
/* How many steps later can the precondition be true? */
val = 0;
while(t+1 < sati->nOfTPoints && fmavalue(sati,precondition,t+1) != 0) {
t = t+1;
val = val + 1;
}
return val;
case 4: /* Evaluate 'constrainedness' of precondition */
/* How many steps later can the precondition be true? */
val = 0;
while(t+1 < sati->nOfTPoints && fmavalue(sati,precondition,t+1) != 0) {
t = t+1;
val = val + 1;
}
return 1000-val;
case 5:
/* How many steps earlier can the precondition be true? */
val = 0;
while(t-1 >= 0 && fmavalue(sati,precondition,t-1) != 0) {
t = t-1;
val = val + 1;
}
return val;
case 6:
/* How many steps earlier can the precondition be true? */
val = 0;
while(t-1 >= 0 && fmavalue(sati,precondition,t-1) != 0) {
t = t-1;
val = val + 1;
}
return 1000-val;
case 7:
/* How many later suggested actions the action disables/affects. */
val = 0;
for(i=0;i<Nsteps;i++) {
if((plansteps[i].t > t) && (actaffects(var,plansteps[i].var))) {
val = val + 1;
}
}
// printf("{%i}",val);
return 1000-val;
default: assert(2==7);
}
}
/* Go through actions at time point t-1 to find one that could
make literal l true at t. "Could" means: l occurs as a conditional
or unconditional effect, and we don't care about the condition.
This is based on the following parameters (given on command line):
HEURtime: which time to consider, 0 = earliest, 1 = latest, 2 = all
HEURops: which operator to consider 0 = first (arbitrary), 1 = all
HEURchoice: which action@time to choose, 0 = random, 1 = weight
*/
/* Choose an action at t1-1 that can make l@t TRUE. The action is returned
in var,t. */
void supports(satinstance sati,int t0,int t1,int l,int *var,int *t,fma **precondition,int *disjunctive) {
compactCEstruct *ptr;
int bestvalue,value;
int bestvar,bestt,bestdisjunctive;
fma *bestprecondition;
if(HEURops == 0) { /* Return the "first" (arbitrary) action. */
for(ptr=cCEs[l];ptr->var != -1;ptr++) { /* All ways of making l true. */
if(!tvarfalsep(sati,ptr->var,t1-1)) { /* Is it applicable? */
#ifdef HARDDEBUG
printf("Add ACTION %i:",ptr->var); printUvar(ptr->var); printf("@%i\n",t1-1);
#endif
*var = ptr->var;
*t = t1-1;
*precondition = ptr->condition;
*disjunctive = ptr->disjunctive;
return;
}
}
assert(1==2);
}
bestvar = -1;
for(ptr=cCEs[l];ptr->var != -1;ptr++) { /* All ways of making l true. */
if(!tvarfalsep(sati,ptr->var,t1-1)) { /* Is it applicable? */
#ifdef HARDDEBUG
printf("Consider ACTION %i:",ptr->var); printUvar(ptr->var); printf("@%i\n",t1-1);
#endif
if(bestvar == -1) {
bestvar = ptr->var;
bestt = t1-1;
bestprecondition = ptr->condition;
bestdisjunctive = ptr->disjunctive;
bestvalue = actionvalue(sati,ptr->var,t1-1,ptr->condition,ptr->disjunctive);
} else {
value = actionvalue(sati,ptr->var,t1-1,ptr->condition,ptr->disjunctive);
if(value > bestvalue) {
bestvar = ptr->var;
bestt = t1-1;
bestprecondition = ptr->condition;
bestdisjunctive = ptr->disjunctive;
bestvalue = value;
}
}
}
}
assert(bestvar != -1);
#ifdef DECDEBUG
printf("Best action: ");
printUvar(bestvar);
printf("@%i for ",bestt);
printlit(l);
printf(" has value %i.\n",bestvalue);
#endif
*var = bestvar;
*t = bestt;
*precondition = bestprecondition;
*disjunctive = bestdisjunctive;
}
/* Is some action op actually making l true at t-1? (= op assigned TRUE?) */
int litmadetrue(satinstance sati,int l,int t,fma **precondition,int *disjunctive) {
int i;
if(!littruep(sati,litwithtime(sati,l,t))) return -1; /* l@t is not even true: no action! */
for(i=0;cCEs[l][i].var != -1;i++) { /* All ways of making l@t-1 true. */
if(tvartruep(sati,cCEs[l][i].var,t-1)) {
*precondition = cCEs[l][i].condition;
*disjunctive = cCEs[l][i].disjunctive;
return cCEs[l][i].var;
}
}
return -1; /* l@t-1 not made true. */
}
/***********************************************************************/
/*** Heuristic: ***/
/*** Find an unfulfilled (sub)goal and fulfill it at the earliest ***/
/*** possible time point. ***/
/*** ***/
/***********************************************************************/
#define noPRUNEfromGOAL
#define noPRUNEtoGOAL
void do_cpath(satinstance sati) {
int l,t,t1;
int j,var,isthere;
int suggestedactionsfound;
int depthlimitHIGH;
fma *precondition;
int disjunctive;
depthlimitHIGH = -1;
suggestedactionsfound = 0;
while(stackptr > 0) {
/* Pop literal-time pair from the stack. */
l = stack[stackptr-1].lit;
t = stack[--stackptr].t;
#ifdef PRUNEtoGOAL
if(HEURactiondepthlimit && suggestedactionsfound && t >= depthlimitHIGH)
return;
#endif
#ifdef HEURDEBUG
printf("Find action for goal "); printlit(l); printf("@%i.\n",t);
#endif
/* Starting from last time point, find last time t such that either
1) an action op@t-1 makes l@t TRUE, or
2) l@t-1 is FALSE and l@t is TRUE or UNASSIGNED.
*/
isthere = 0;
var = -1;
for(j=t;j>0;j--) {
var = litmadetrue(sati,l,j,&precondition,&disjunctive);
if(var != -1) {
isthere = 1;
t1 = j-1;
break;
}
if(litfalsep(sati,litwithtime(sati,l,j-1))) break;
}
if(j == 0) { /* Literal is true at time 0. */
#ifdef HEURDEBUG
printf("(true in the initial state.)\n");
#endif
continue;
}
/* Literal is last false at time point j-1. */
if(var == -1) { /* Choose an action that turns l true between j-1 and j. */
supports(sati,t,j,l,&var,&t1,&precondition,&disjunctive);
/* The following assertions cannot be false because
there must be an action that could make l true at t1 (i.e. is not FALSE).
If there were not, the frame action (-l & l) -> ... would
contradict Ass1 and Ass2.
*/
ASSERT(var != 0);
ASSERT(precondition->t != -1);
ASSERT(tlitfalsep(sati,l,j-1));
ASSERT(!tlitfalsep(sati,l,j));
}
if(seenp(sati,TVAR(var,t1))) continue; /* Operator already processed once. */
/* Don't go back to top-level goals. */
#ifndef PRUNEtoGOAL
if(HEURactiondepthlimit && suggestedactionsfound && t1 >= depthlimitHIGH)
return;
#endif
if(!isthere) { /* Add the variable to the list of candidate decisions. */
suggestedactionsfound += 1;
plansteps[Nsteps].var = var;
plansteps[Nsteps].t = t1;
plansteps[Nsteps].flaws = 0;
if(HEURops == 7) {
for(j=0;j<Nsteps;j++) {
if(actaffects(var,plansteps[j].var)) plansteps[Nsteps].flaws += 1;
}
}
plansteps[Nsteps++].l = l;
#ifdef HEURDEBUG
printf("Chose action ");
printUvar(var);
printf(" to make literal true @%i.\n",j);
#endif
#ifdef PRUNEfromGOAL
if(suggestedactionsfound == 1) depthlimitHIGH = t;
#else
if(suggestedactionsfound == 1) depthlimitHIGH = t1;
#endif
}
ASSERT(Nsteps < MAXPLANSTEPS);
if(suggestedactionsfound >= HEURactions) return;
#ifdef HARDDEBUG
printf(" Push preconditions of %i:",var); printUvar(var); printf("@%i into the stack (%i).\n",t1,isthere);
#endif
if(t1 > 0) { /* Push preconditions in the stack. */
if(disjunctive == 0) {
push2goalstackCfma(sati,precondition,t1);
} else {
push2goalstackDfma(sati,precondition,t1);
}
#ifdef HARDDEBUG
showheustack(sati);
#endif
}
}
}