ltl2ba-nix/generalized.c
2020-10-25 12:07:44 +01:00

657 lines
20 KiB
C

/***** ltl2ba : generalized.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 <sys/time.h>
#include <sys/resource.h>
/********************************************************************\
|* Structures and shared variables *|
\********************************************************************/
extern FILE *tl_out;
extern ATrans **transition;
extern struct rusage tr_debut, tr_fin;
extern struct timeval t_diff;
extern int tl_verbose, tl_stats, tl_simp_diff, tl_simp_fly, tl_fjtofj,
tl_simp_scc, *final_set, node_id;
extern char **sym_table;
GState *gstack, *gremoved, *gstates, **init;
static GScc *scc_stack;
int init_size = 0, gstate_id = 1, gstate_count = 0, gtrans_count = 0;
int *fin, *final, scc_id, scc_size, *bad_scc;
static int rank;
void print_generalized();
/********************************************************************\
|* Simplification of the generalized Buchi automaton *|
\********************************************************************/
void free_gstate(GState *s) /* frees a state and its transitions */
{
free_gtrans(s->trans->nxt, s->trans, 1);
tfree(s->nodes_set);
tfree(s);
}
GState *remove_gstate(GState *s, GState *s1) /* removes a state */
{
GState *prv = s->prv;
s->prv->nxt = s->nxt;
s->nxt->prv = s->prv;
free_gtrans(s->trans->nxt, s->trans, 0);
s->trans = (GTrans *)0;
tfree(s->nodes_set);
s->nodes_set = 0;
s->nxt = gremoved->nxt;
gremoved->nxt = s;
s->prv = s1;
for(s1 = gremoved->nxt; s1 != gremoved; s1 = s1->nxt)
if(s1->prv == s)
s1->prv = s->prv;
return prv;
}
void copy_gtrans(GTrans *from, GTrans *to) /* copies a transition */
{
to->to = from->to;
copy_set(from->pos, to->pos, 1);
copy_set(from->neg, to->neg, 1);
copy_set(from->final, to->final, 0);
}
int same_gtrans(GState *a, GTrans *s, GState *b, GTrans *t, int use_scc)
{ /* returns 1 if the transitions are identical */
if((s->to != t->to) ||
! same_sets(s->pos, t->pos, 1) ||
! same_sets(s->neg, t->neg, 1))
return 0; /* transitions differ */
if(same_sets(s->final, t->final, 0))
return 1; /* same transitions exactly */
/* next we check whether acceptance conditions may be ignored */
if( use_scc &&
( in_set(bad_scc, a->incoming) ||
in_set(bad_scc, b->incoming) ||
(a->incoming != s->to->incoming) ||
(b->incoming != t->to->incoming) ) )
return 1;
return 0;
/* below is the old test to check whether acceptance conditions may be ignored */
if(!use_scc)
return 0; /* transitions differ */
if( (a->incoming == b->incoming) && (a->incoming == s->to->incoming) )
return 0; /* same scc: acceptance conditions must be taken into account */
/* if scc(a)=scc(b)>scc(s->to) then acceptance conditions need not be taken into account */
/* if scc(a)>scc(b) and scc(a) is non-trivial then all_gtrans_match(a,b,use_scc) will fail */
/* if scc(a) is trivial then acceptance conditions of transitions from a need not be taken into account */
return 1; /* same transitions up to acceptance conditions */
}
int simplify_gtrans() /* simplifies the transitions */
{
int changed = 0;
GState *s;
GTrans *t, *t1;
if(tl_stats) getrusage(RUSAGE_SELF, &tr_debut);
for(s = gstates->nxt; s != gstates; s = s->nxt) {
t = s->trans->nxt;
while(t != s->trans) { /* tries to remove t */
copy_gtrans(t, s->trans);
t1 = s->trans->nxt;
while ( !((t != t1)
&& (t1->to == t->to)
&& included_set(t1->pos, t->pos, 1)
&& included_set(t1->neg, t->neg, 1)
&& (included_set(t->final, t1->final, 0) /* acceptance conditions of t are also in t1 or may be ignored */
|| (tl_simp_scc && ((s->incoming != t->to->incoming) || in_set(bad_scc, s->incoming))))) )
t1 = t1->nxt;
if(t1 != s->trans) { /* remove transition t */
GTrans *free = t->nxt;
t->to = free->to;
copy_set(free->pos, t->pos, 1);
copy_set(free->neg, t->neg, 1);
copy_set(free->final, t->final, 0);
t->nxt = free->nxt;
if(free == s->trans) s->trans = t;
free_gtrans(free, 0, 0);
changed++;
}
else
t = t->nxt;
}
}
if(tl_stats) {
getrusage(RUSAGE_SELF, &tr_fin);
timeval_subtract (&t_diff, &tr_fin.ru_utime, &tr_debut.ru_utime);
fprintf(tl_out, "\nSimplification of the generalized Buchi automaton - transitions: %lli.%06ld",
(long long int)t_diff.tv_sec, t_diff.tv_usec);
fprintf(tl_out, "\n%i transitions removed\n", changed);
}
return changed;
}
void retarget_all_gtrans()
{ /* redirects transitions before removing a state from the automaton */
GState *s;
GTrans *t;
int i;
for (i = 0; i < init_size; i++)
if (init[i] && !init[i]->trans) /* init[i] has been removed */
init[i] = init[i]->prv;
for (s = gstates->nxt; s != gstates; s = s->nxt)
for (t = s->trans->nxt; t != s->trans; )
if (!t->to->trans) { /* t->to has been removed */
t->to = t->to->prv;
if(!t->to) { /* t->to has no transitions */
GTrans *free = t->nxt;
t->to = free->to;
copy_set(free->pos, t->pos, 1);
copy_set(free->neg, t->neg, 1);
copy_set(free->final, t->final, 0);
t->nxt = free->nxt;
if(free == s->trans) s->trans = t;
free_gtrans(free, 0, 0);
}
else
t = t->nxt;
}
else
t = t->nxt;
while(gremoved->nxt != gremoved) { /* clean the 'removed' list */
s = gremoved->nxt;
gremoved->nxt = gremoved->nxt->nxt;
if(s->nodes_set) tfree(s->nodes_set);
tfree(s);
}
}
int all_gtrans_match(GState *a, GState *b, int use_scc)
{ /* decides if the states are equivalent */
GTrans *s, *t;
for (s = a->trans->nxt; s != a->trans; s = s->nxt) {
/* all transitions from a appear in b */
copy_gtrans(s, b->trans);
t = b->trans->nxt;
while(!same_gtrans(a, s, b, t, use_scc)) t = t->nxt;
if(t == b->trans) return 0;
}
for (t = b->trans->nxt; t != b->trans; t = t->nxt) {
/* all transitions from b appear in a */
copy_gtrans(t, a->trans);
s = a->trans->nxt;
while(!same_gtrans(a, s, b, t, use_scc)) s = s->nxt;
if(s == a->trans) return 0;
}
return 1;
}
int simplify_gstates() /* eliminates redundant states */
{
int changed = 0;
GState *a, *b;
if(tl_stats) getrusage(RUSAGE_SELF, &tr_debut);
for(a = gstates->nxt; a != gstates; a = a->nxt) {
if(a->trans == a->trans->nxt) { /* a has no transitions */
a = remove_gstate(a, (GState *)0);
changed++;
continue;
}
gstates->trans = a->trans;
b = a->nxt;
while(!all_gtrans_match(a, b, tl_simp_scc)) b = b->nxt;
if(b != gstates) { /* a and b are equivalent */
/* if scc(a)>scc(b) and scc(a) is non-trivial then all_gtrans_match(a,b,use_scc) must fail */
if(a->incoming > b->incoming) /* scc(a) is trivial */
a = remove_gstate(a, b);
else /* either scc(a)=scc(b) or scc(b) is trivial */
remove_gstate(b, a);
changed++;
}
}
retarget_all_gtrans();
if(tl_stats) {
getrusage(RUSAGE_SELF, &tr_fin);
timeval_subtract (&t_diff, &tr_fin.ru_utime, &tr_debut.ru_utime);
fprintf(tl_out, "\nSimplification of the generalized Buchi automaton - states: %lli.%06ld",
(long long int)t_diff.tv_sec, t_diff.tv_usec);
fprintf(tl_out, "\n%i states removed\n", changed);
}
return changed;
}
int gdfs(GState *s) {
GTrans *t;
GScc *c;
GScc *scc = (GScc *)tl_emalloc(sizeof(GScc));
scc->gstate = s;
scc->rank = rank;
scc->theta = rank++;
scc->nxt = scc_stack;
scc_stack = scc;
s->incoming = 1;
for (t = s->trans->nxt; t != s->trans; t = t->nxt) {
if (t->to->incoming == 0) {
int result = gdfs(t->to);
scc->theta = min(scc->theta, result);
}
else {
for(c = scc_stack->nxt; c != 0; c = c->nxt)
if(c->gstate == t->to) {
scc->theta = min(scc->theta, c->rank);
break;
}
}
}
if(scc->rank == scc->theta) {
while(scc_stack != scc) {
scc_stack->gstate->incoming = scc_id;
scc_stack = scc_stack->nxt;
}
scc->gstate->incoming = scc_id++;
scc_stack = scc->nxt;
}
return scc->theta;
}
void simplify_gscc() {
GState *s;
GTrans *t;
int i, **scc_final;
rank = 1;
scc_stack = 0;
scc_id = 1;
if(gstates == gstates->nxt) return;
for(s = gstates->nxt; s != gstates; s = s->nxt)
s->incoming = 0; /* state color = white */
for(i = 0; i < init_size; i++)
if(init[i] && init[i]->incoming == 0)
gdfs(init[i]);
scc_final = (int **)tl_emalloc(scc_id * sizeof(int *));
for(i = 0; i < scc_id; i++)
scc_final[i] = make_set(-1,0);
for(s = gstates->nxt; s != gstates; s = s->nxt)
if(s->incoming == 0)
s = remove_gstate(s, 0);
else
for (t = s->trans->nxt; t != s->trans; t = t->nxt)
if(t->to->incoming == s->incoming)
merge_sets(scc_final[s->incoming], t->final, 0);
scc_size = (scc_id + 1) / (8 * sizeof(int)) + 1;
bad_scc=make_set(-1,2);
for(i = 0; i < scc_id; i++)
if(!included_set(final_set, scc_final[i], 0))
add_set(bad_scc, i);
for(i = 0; i < scc_id; i++)
tfree(scc_final[i]);
tfree(scc_final);
}
/********************************************************************\
|* Generation of the generalized Buchi automaton *|
\********************************************************************/
int is_final(int *from, ATrans *at, int i) /*is the transition final for i ?*/
{
ATrans *t;
int in_to;
if((tl_fjtofj && !in_set(at->to, i)) ||
(!tl_fjtofj && !in_set(from, i))) return 1;
in_to = in_set(at->to, i);
rem_set(at->to, i);
for(t = transition[i]; t; t = t->nxt)
if(included_set(t->to, at->to, 0) &&
included_set(t->pos, at->pos, 1) &&
included_set(t->neg, at->neg, 1)) {
if(in_to) add_set(at->to, i);
return 1;
}
if(in_to) add_set(at->to, i);
return 0;
}
GState *find_gstate(int *set, GState *s)
{ /* finds the corresponding state, or creates it */
if(same_sets(set, s->nodes_set, 0)) return s; /* same state */
s = gstack->nxt; /* in the stack */
gstack->nodes_set = set;
while(!same_sets(set, s->nodes_set, 0))
s = s->nxt;
if(s != gstack) return s;
s = gstates->nxt; /* in the solved states */
gstates->nodes_set = set;
while(!same_sets(set, s->nodes_set, 0))
s = s->nxt;
if(s != gstates) return s;
s = gremoved->nxt; /* in the removed states */
gremoved->nodes_set = set;
while(!same_sets(set, s->nodes_set, 0))
s = s->nxt;
if(s != gremoved) return s;
s = (GState *)tl_emalloc(sizeof(GState)); /* creates a new state */
s->id = (empty_set(set, 0)) ? 0 : gstate_id++;
s->incoming = 0;
s->nodes_set = dup_set(set, 0);
s->trans = emalloc_gtrans(); /* sentinel */
s->trans->nxt = s->trans;
s->nxt = gstack->nxt;
gstack->nxt = s;
return s;
}
void make_gtrans(GState *s) { /* creates all the transitions from a state */
int i, *list, state_trans = 0, trans_exist = 1;
GState *s1;
ATrans *t1;
AProd *prod = (AProd *)tl_emalloc(sizeof(AProd)); /* initialization */
prod->nxt = prod;
prod->prv = prod;
prod->prod = emalloc_atrans();
clear_set(prod->prod->to, 0);
clear_set(prod->prod->pos, 1);
clear_set(prod->prod->neg, 1);
prod->trans = prod->prod;
prod->trans->nxt = prod->prod;
list = list_set(s->nodes_set, 0);
for(i = 1; i < list[0]; i++) {
AProd *p = (AProd *)tl_emalloc(sizeof(AProd));
p->astate = list[i];
p->trans = transition[list[i]];
if(!p->trans) trans_exist = 0;
p->prod = merge_trans(prod->nxt->prod, p->trans);
p->nxt = prod->nxt;
p->prv = prod;
p->nxt->prv = p;
p->prv->nxt = p;
}
while(trans_exist) { /* calculates all the transitions */
AProd *p = prod->nxt;
t1 = p->prod;
if(t1) { /* solves the current transition */
GTrans *trans, *t2;
clear_set(fin, 0);
for(i = 1; i < final[0]; i++)
if(is_final(s->nodes_set, t1, final[i]))
add_set(fin, final[i]);
for(t2 = s->trans->nxt; t2 != s->trans;) {
if(tl_simp_fly &&
included_set(t1->to, t2->to->nodes_set, 0) &&
included_set(t1->pos, t2->pos, 1) &&
included_set(t1->neg, t2->neg, 1) &&
same_sets(fin, t2->final, 0)) { /* t2 is redondant */
GTrans *free = t2->nxt;
t2->to->incoming--;
t2->to = free->to;
copy_set(free->pos, t2->pos, 1);
copy_set(free->neg, t2->neg, 1);
copy_set(free->final, t2->final, 0);
t2->nxt = free->nxt;
if(free == s->trans) s->trans = t2;
free_gtrans(free, 0, 0);
state_trans--;
}
else if(tl_simp_fly &&
included_set(t2->to->nodes_set, t1->to, 0) &&
included_set(t2->pos, t1->pos, 1) &&
included_set(t2->neg, t1->neg, 1) &&
same_sets(t2->final, fin, 0)) {/* t1 is redondant */
break;
}
else {
t2 = t2->nxt;
}
}
if(t2 == s->trans) { /* adds the transition */
trans = emalloc_gtrans();
trans->to = find_gstate(t1->to, s);
trans->to->incoming++;
copy_set(t1->pos, trans->pos, 1);
copy_set(t1->neg, trans->neg, 1);
copy_set(fin, trans->final, 0);
trans->nxt = s->trans->nxt;
s->trans->nxt = trans;
state_trans++;
}
}
if(!p->trans)
break;
while(!p->trans->nxt) /* calculates the next transition */
p = p->nxt;
if(p == prod)
break;
p->trans = p->trans->nxt;
do_merge_trans(&(p->prod), p->nxt->prod, p->trans);
p = p->prv;
while(p != prod) {
p->trans = transition[p->astate];
do_merge_trans(&(p->prod), p->nxt->prod, p->trans);
p = p->prv;
}
}
tfree(list); /* free memory */
while(prod->nxt != prod) {
AProd *p = prod->nxt;
prod->nxt = p->nxt;
free_atrans(p->prod, 0);
tfree(p);
}
free_atrans(prod->prod, 0);
tfree(prod);
if(tl_simp_fly) {
if(s->trans == s->trans->nxt) { /* s has no transitions */
free_gtrans(s->trans->nxt, s->trans, 1);
s->trans = (GTrans *)0;
s->prv = (GState *)0;
s->nxt = gremoved->nxt;
gremoved->nxt = s;
for(s1 = gremoved->nxt; s1 != gremoved; s1 = s1->nxt)
if(s1->prv == s)
s1->prv = (GState *)0;
return;
}
gstates->trans = s->trans;
s1 = gstates->nxt;
while(!all_gtrans_match(s, s1, 0))
s1 = s1->nxt;
if(s1 != gstates) { /* s and s1 are equivalent */
free_gtrans(s->trans->nxt, s->trans, 1);
s->trans = (GTrans *)0;
s->prv = s1;
s->nxt = gremoved->nxt;
gremoved->nxt = s;
for(s1 = gremoved->nxt; s1 != gremoved; s1 = s1->nxt)
if(s1->prv == s)
s1->prv = s->prv;
return;
}
}
s->nxt = gstates->nxt; /* adds the current state to 'gstates' */
s->prv = gstates;
s->nxt->prv = s;
gstates->nxt = s;
gtrans_count += state_trans;
gstate_count++;
}
/********************************************************************\
|* Display of the generalized Buchi automaton *|
\********************************************************************/
void reverse_print_generalized(GState *s) /* dumps the generalized Buchi automaton */
{
GTrans *t;
if(s == gstates) return;
reverse_print_generalized(s->nxt); /* begins with the last state */
fprintf(tl_out, "state %i (", s->id);
print_set(s->nodes_set, 0);
fprintf(tl_out, ") : %i\n", s->incoming);
for(t = s->trans->nxt; t != s->trans; 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, 1);
fprintf(tl_out, " -> %i : ", t->to->id);
print_set(t->final, 0);
fprintf(tl_out, "\n");
}
}
void print_generalized() { /* prints intial states and calls 'reverse_print' */
int i;
fprintf(tl_out, "init :\n");
for(i = 0; i < init_size; i++)
if(init[i])
fprintf(tl_out, "%i\n", init[i]->id);
reverse_print_generalized(gstates->nxt);
}
/********************************************************************\
|* Main method *|
\********************************************************************/
void mk_generalized()
{ /* generates a generalized Buchi automaton from the alternating automaton */
ATrans *t;
GState *s;
if(tl_stats) getrusage(RUSAGE_SELF, &tr_debut);
fin = new_set(0);
bad_scc = 0; /* will be initialized in simplify_gscc */
final = list_set(final_set, 0);
gstack = (GState *)tl_emalloc(sizeof(GState)); /* sentinel */
gstack->nxt = gstack;
gremoved = (GState *)tl_emalloc(sizeof(GState)); /* sentinel */
gremoved->nxt = gremoved;
gstates = (GState *)tl_emalloc(sizeof(GState)); /* sentinel */
gstates->nxt = gstates;
gstates->prv = gstates;
for(t = transition[0]; t; t = t->nxt) { /* puts initial states in the stack */
s = (GState *)tl_emalloc(sizeof(GState));
s->id = (empty_set(t->to, 0)) ? 0 : gstate_id++;
s->incoming = 1;
s->nodes_set = dup_set(t->to, 0);
s->trans = emalloc_gtrans(); /* sentinel */
s->trans->nxt = s->trans;
s->nxt = gstack->nxt;
gstack->nxt = s;
init_size++;
}
if(init_size) init = (GState **)tl_emalloc(init_size * sizeof(GState *));
init_size = 0;
for(s = gstack->nxt; s != gstack; s = s->nxt)
init[init_size++] = s;
while(gstack->nxt != gstack) { /* solves all states in the stack until it is empty */
s = gstack->nxt;
gstack->nxt = gstack->nxt->nxt;
if(!s->incoming) {
free_gstate(s);
continue;
}
make_gtrans(s);
}
retarget_all_gtrans();
if(tl_stats) {
getrusage(RUSAGE_SELF, &tr_fin);
timeval_subtract (&t_diff, &tr_fin.ru_utime, &tr_debut.ru_utime);
fprintf(tl_out, "\nBuilding the generalized Buchi automaton : %lli.%06ld",
(long long int)t_diff.tv_sec, t_diff.tv_usec);
fprintf(tl_out, "\n%i states, %i transitions\n", gstate_count, gtrans_count);
}
tfree(gstack);
/*for(i = 0; i < node_id; i++) / * frees the data from the alternating automaton */
/*free_atrans(transition[i], 1);*/
free_all_atrans();
tfree(transition);
if(tl_verbose) {
fprintf(tl_out, "\nGeneralized Buchi automaton before simplification\n");
print_generalized();
}
if(tl_simp_diff) {
if (tl_simp_scc) simplify_gscc();
simplify_gtrans();
if (tl_simp_scc) simplify_gscc();
while(simplify_gstates()) { /* simplifies as much as possible */
if (tl_simp_scc) simplify_gscc();
simplify_gtrans();
if (tl_simp_scc) simplify_gscc();
}
if(tl_verbose) {
fprintf(tl_out, "\nGeneralized Buchi automaton after simplification\n");
print_generalized();
}
}
}