/***** ltl2ba : alternating.c *****/ /* Written by Denis Oddoux, LIAFA, France */ /* Copyright (c) 2001 Denis Oddoux */ /* Modified by Paul Gastin, LSV, France */ /* Copyright (c) 2007 Paul Gastin */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/ /* */ /* Based on the translation algorithm by Gastin and Oddoux, */ /* presented at the 13th International Conference on Computer Aided */ /* Verification, CAV 2001, Paris, France. */ /* Proceedings - LNCS 2102, pp. 53-65 */ /* */ /* Send bug-reports and/or questions to Paul Gastin */ /* http://www.lsv.ens-cachan.fr/~gastin */ #include "config.h" #include "ltl2ba.h" #include "util.h" #include /********************************************************************\ |* Structures and shared variables *| \********************************************************************/ extern FILE *tl_out; extern int tl_verbose, tl_stats, tl_simp_diff; Node **label; char **sym_table; ATrans **transition; struct rusage tr_debut, tr_fin; struct timeval t_diff; int *final_set, node_id = 1, sym_id = 0, node_size, sym_size, n_sym; int astate_count = 0, atrans_count = 0; ATrans *build_alternating(Node *p); /********************************************************************\ |* Generation of the alternating automaton *| \********************************************************************/ int calculate_node_size(Node *p) /* returns the number of temporal nodes */ { switch(p->ntyp) { case AND: case OR: case U_OPER: case V_OPER: return(calculate_node_size(p->lft) + calculate_node_size(p->rgt) + 1); #ifdef NXT case NEXT: return(calculate_node_size(p->lft) + 1); #endif default: return 1; break; } } int calculate_sym_size(Node *p) /* returns the number of predicates */ { switch(p->ntyp) { case AND: case OR: case U_OPER: case V_OPER: return(calculate_sym_size(p->lft) + calculate_sym_size(p->rgt)); #ifdef NXT case NEXT: return(calculate_sym_size(p->lft)); #endif case NOT: case PREDICATE: return 1; default: return 0; } } ATrans *dup_trans(ATrans *trans) /* returns the copy of a transition */ { ATrans *result; if(!trans) return trans; result = emalloc_atrans(); copy_set(trans->to, result->to, 0); copy_set(trans->pos, result->pos, 1); copy_set(trans->neg, result->neg, 1); return result; } void do_merge_trans(ATrans **result, ATrans *trans1, ATrans *trans2) { /* merges two transitions */ if(!trans1 || !trans2) { free_atrans(*result, 0); *result = (ATrans *)0; return; } if(!*result) *result = emalloc_atrans(); do_merge_sets((*result)->to, trans1->to, trans2->to, 0); do_merge_sets((*result)->pos, trans1->pos, trans2->pos, 1); do_merge_sets((*result)->neg, trans1->neg, trans2->neg, 1); if(!empty_intersect_sets((*result)->pos, (*result)->neg, 1)) { free_atrans(*result, 0); *result = (ATrans *)0; } } ATrans *merge_trans(ATrans *trans1, ATrans *trans2) /* merges two transitions */ { ATrans *result = emalloc_atrans(); do_merge_trans(&result, trans1, trans2); return result; } int already_done(Node *p) /* finds the id of the node, if already explored */ { int i; for(i = 1; i next & init */ { ATrans *t1, *t2, *lft, *rgt, *result = (ATrans *)0; switch(p->ntyp) { case TRUE: result = emalloc_atrans(); clear_set(result->to, 0); clear_set(result->pos, 1); clear_set(result->neg, 1); case FALSE: break; case AND: lft = _boolean(p->lft); rgt = _boolean(p->rgt); for(t1 = lft; t1; t1 = t1->nxt) { for(t2 = rgt; t2; t2 = t2->nxt) { ATrans *tmp = merge_trans(t1, t2); if(tmp) { tmp->nxt = result; result = tmp; } } } free_atrans(lft, 1); free_atrans(rgt, 1); break; case OR: lft = _boolean(p->lft); for(t1 = lft; t1; t1 = t1->nxt) { ATrans *tmp = dup_trans(t1); tmp->nxt = result; result = tmp; } free_atrans(lft, 1); rgt = _boolean(p->rgt); for(t1 = rgt; t1; t1 = t1->nxt) { ATrans *tmp = dup_trans(t1); tmp->nxt = result; result = tmp; } free_atrans(rgt, 1); break; default: build_alternating(p); result = emalloc_atrans(); clear_set(result->to, 0); clear_set(result->pos, 1); clear_set(result->neg, 1); add_set(result->to, already_done(p)); } return result; } ATrans *build_alternating(Node *p) /* builds an alternating automaton for p */ { ATrans *t1, *t2, *t = (ATrans *)0; int node = already_done(p); if(node >= 0) return transition[node]; switch (p->ntyp) { case TRUE: t = emalloc_atrans(); clear_set(t->to, 0); clear_set(t->pos, 1); clear_set(t->neg, 1); case FALSE: break; case PREDICATE: t = emalloc_atrans(); clear_set(t->to, 0); clear_set(t->pos, 1); clear_set(t->neg, 1); add_set(t->pos, get_sym_id(p->sym->name)); break; case NOT: t = emalloc_atrans(); clear_set(t->to, 0); clear_set(t->pos, 1); clear_set(t->neg, 1); add_set(t->neg, get_sym_id(p->lft->sym->name)); break; #ifdef NXT case NEXT: t = _boolean(p->lft); break; #endif case U_OPER: /* p U q <-> q || (p && X (p U q)) */ for(t2 = build_alternating(p->rgt); t2; t2 = t2->nxt) { ATrans *tmp = dup_trans(t2); /* q */ tmp->nxt = t; t = tmp; } for(t1 = build_alternating(p->lft); t1; t1 = t1->nxt) { ATrans *tmp = dup_trans(t1); /* p */ add_set(tmp->to, node_id); /* X (p U q) */ tmp->nxt = t; t = tmp; } add_set(final_set, node_id); break; case V_OPER: /* p V q <-> (p && q) || (p && X (p V q)) */ for(t1 = build_alternating(p->rgt); t1; t1 = t1->nxt) { ATrans *tmp; for(t2 = build_alternating(p->lft); t2; t2 = t2->nxt) { tmp = merge_trans(t1, t2); /* p && q */ if(tmp) { tmp->nxt = t; t = tmp; } } tmp = dup_trans(t1); /* p */ add_set(tmp->to, node_id); /* X (p V q) */ tmp->nxt = t; t = tmp; } break; case AND: t = (ATrans *)0; for(t1 = build_alternating(p->lft); t1; t1 = t1->nxt) { for(t2 = build_alternating(p->rgt); t2; t2 = t2->nxt) { ATrans *tmp = merge_trans(t1, t2); if(tmp) { tmp->nxt = t; t = tmp; } } } break; case OR: t = (ATrans *)0; for(t1 = build_alternating(p->lft); t1; t1 = t1->nxt) { ATrans *tmp = dup_trans(t1); tmp->nxt = t; t = tmp; } for(t1 = build_alternating(p->rgt); t1; t1 = t1->nxt) { ATrans *tmp = dup_trans(t1); tmp->nxt = t; t = tmp; } break; default: break; } transition[node_id] = t; label[node_id++] = p; return(t); } /********************************************************************\ |* Simplification of the alternating automaton *| \********************************************************************/ void simplify_atrans(ATrans **trans) /* simplifies the transitions */ { ATrans *t, *father = (ATrans *)0; for(t = *trans; t;) { ATrans *t1; for(t1 = *trans; t1; t1 = t1->nxt) { if((t1 != t) && included_set(t1->to, t->to, 0) && included_set(t1->pos, t->pos, 1) && included_set(t1->neg, t->neg, 1)) break; } if(t1) { if (father) father->nxt = t->nxt; else *trans = t->nxt; free_atrans(t, 0); if (father) t = father->nxt; else t = *trans; continue; } atrans_count++; father = t; t = t->nxt; } } void simplify_astates() /* simplifies the alternating automaton */ { ATrans *t; int i, *acc = make_set(-1, 0); /* no state is accessible initially */ for(t = transition[0]; t; t = t->nxt, i = 0) merge_sets(acc, t->to, 0); /* all initial states are accessible */ for(i = node_id - 1; i > 0; i--) { if (!in_set(acc, i)) { /* frees unaccessible states */ label[i] = ZN; free_atrans(transition[i], 1); transition[i] = (ATrans *)0; continue; } astate_count++; simplify_atrans(&transition[i]); for(t = transition[i]; t; t = t->nxt) merge_sets(acc, t->to, 0); } tfree(acc); } /********************************************************************\ |* Display of the alternating automaton *| \********************************************************************/ void print_alternating() /* dumps the alternating automaton */ { int i; ATrans *t; fprintf(tl_out, "init :\n"); for(t = transition[0]; t; t = t->nxt) { print_set(t->to, 0); fprintf(tl_out, "\n"); } for(i = node_id - 1; i > 0; i--) { if(!label[i]) continue; fprintf(tl_out, "state %i : ", i); dump(label[i]); fprintf(tl_out, "\n"); for(t = transition[i]; t; t = t->nxt) { if (empty_set(t->pos, 1) && empty_set(t->neg, 1)) fprintf(tl_out, "1"); print_set(t->pos, 1); if (!empty_set(t->pos,1) && !empty_set(t->neg,1)) fprintf(tl_out, " & "); print_set(t->neg, 2); fprintf(tl_out, " -> "); print_set(t->to, 0); fprintf(tl_out, "\n"); } } } /********************************************************************\ |* Main method *| \********************************************************************/ void mk_alternating(Node *p) /* generates an alternating automaton for p */ { if(tl_stats) getrusage(RUSAGE_SELF, &tr_debut); node_size = calculate_node_size(p) + 1; /* number of states in the automaton */ label = (Node **) tl_emalloc(node_size * sizeof(Node *)); transition = (ATrans **) tl_emalloc(node_size * sizeof(ATrans *)); node_size = node_size / (8 * sizeof(int)) + 1; n_sym = calculate_sym_size(p); /* number of predicates */ if(n_sym) sym_table = (char **) tl_emalloc(n_sym * sizeof(char *)); sym_size = n_sym / (8 * sizeof(int)) + 1; final_set = make_set(-1, 0); transition[0] = _boolean(p); /* generates the alternating automaton */ if(tl_verbose) { fprintf(tl_out, "\nAlternating automaton before simplification\n"); print_alternating(); } if(tl_simp_diff) { simplify_astates(); /* keeps only accessible states */ if(tl_verbose) { fprintf(tl_out, "\nAlternating automaton after simplification\n"); print_alternating(); } } if(tl_stats) { getrusage(RUSAGE_SELF, &tr_fin); timeval_subtract (&t_diff, &tr_fin.ru_utime, &tr_debut.ru_utime); fprintf(tl_out, "\nBuilding and simplification of the alternating automaton: %lli.%06ld", (long long int)t_diff.tv_sec, t_diff.tv_usec); fprintf(tl_out, "\n%i states, %i transitions\n", astate_count, atrans_count); } releasenode(1, p); tfree(label); }