Skip to content

Commit 4c01e47

Browse files
committed
LTL/SVA->Buechi: reject error traces early
The Buechi automaton accepts counterexamples to the original property. The Buechi acceptance condition requires a lasso where the loop contains an accepting state. A proof of that requires satisfying the looping condition, which may require a very long counterexample. This adds a shortcut using a sufficient condition for a counterexample: we can reject the property when we reach a state in which the Buechi automaton transitions into an accepting state that has an unconditional self-loop on it. Such traces can always be extended to a lasso as above.
1 parent ad2f8ba commit 4c01e47

File tree

6 files changed

+71
-7
lines changed

6 files changed

+71
-7
lines changed

regression/ebmc-spot/sva-buechi/followed-by4.bmc.desc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
../../verilog/SVA/followed-by4.sv
33
--buechi --bound 3
44
^\[main\.p1\] always \(main\.x == 0 #-# 1\): REFUTED$
@@ -9,4 +9,3 @@ KNOWNBUG
99
--
1010
^warning: ignoring
1111
--
12-
p1 is not refuted.
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
../../verilog/SVA/s_always1.sv
33
--buechi --bound 20
44
^\[main\.p0\] s_always \[0:9\] main\.x < 10: PROVED up to bound 20$
@@ -8,4 +8,3 @@ KNOWNBUG
88
--
99
^warning: ignoring
1010
--
11-
main.p1 is not refuted.

src/ebmc/instrument_buechi.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Author: Daniel Kroening, dkr@amazon.com
88

99
#include "instrument_buechi.h"
1010

11+
#include <util/format_expr.h>
12+
1113
#include <temporal-logic/ltl.h>
1214
#include <temporal-logic/ltl_to_buechi.h>
1315
#include <temporal-logic/temporal_logic.h>
@@ -67,5 +69,12 @@ void instrument_buechi(
6769
// so this is the negation of the Buechi acceptance condition.
6870
property.normalized_expr =
6971
F_exprt{G_exprt{not_exprt{buechi.liveness_signal}}};
72+
73+
if(!buechi.error_signal.is_true())
74+
property.normalized_expr = and_exprt{
75+
property.normalized_expr, G_exprt{not_exprt{buechi.error_signal}}};
76+
77+
message.debug() << "New property: " << format(property.normalized_expr)
78+
<< messaget::eom;
7079
}
7180
}

src/temporal-logic/hoa.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class hoat
3232
mp_integer number;
3333
labelt label; // in-state condition
3434
acc_sigt acc_sig;
35+
bool is_accepting() const
36+
{
37+
return !acc_sig.empty();
38+
}
3539
};
3640
struct edget
3741
{

src/temporal-logic/ltl_to_buechi.cpp

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Author: Daniel Kroening, dkr@amazon.com
99
#include "ltl_to_buechi.h"
1010

1111
#include <util/arith_tools.h>
12+
#include <util/expr_util.h>
1213
#include <util/format_expr.h>
1314
#include <util/message.h>
1415
#include <util/replace_expr.h>
@@ -37,6 +38,7 @@ void buechi_transt::rename_state_symbol(const symbol_exprt &new_state_symbol)
3738

3839
replace_expr(replace_map, init);
3940
replace_expr(replace_map, trans);
41+
replace_expr(replace_map, error_signal);
4042
replace_expr(replace_map, liveness_signal);
4143

4244
state_symbol = new_state_symbol;
@@ -81,6 +83,23 @@ exprt hoa_label_to_expr(
8183
}
8284
}
8385

86+
// an accepting state with an unconditional self-loop
87+
bool is_error_state(const std::pair<hoat::state_namet, hoat::edgest> &state)
88+
{
89+
if(!state.first.is_accepting())
90+
return false;
91+
92+
for(auto &edge : state.second)
93+
{
94+
if(edge.label.id() == "t")
95+
for(auto &dest : edge.dest_states)
96+
if(dest == state.first.number)
97+
return true;
98+
}
99+
100+
return false;
101+
}
102+
84103
buechi_transt
85104
ltl_to_buechi(const exprt &property, message_handlert &message_handler)
86105
{
@@ -100,8 +119,7 @@ ltl_to_buechi(const exprt &property, message_handlert &message_handler)
100119

101120
// State-based Buchi acceptance. Should compare with transition-based
102121
// acceptance.
103-
// Use --complete to be able to have multiple properties in one
104-
// model.
122+
// The generated automata may have deadend states.
105123
auto run_result = run(
106124
"ltl2tgba",
107125
{"ltl2tgba", "--sba", "--complete", "--hoaf=1.1", string},
@@ -144,7 +162,7 @@ ltl_to_buechi(const exprt &property, message_handlert &message_handler)
144162
std::vector<exprt> liveness_disjuncts;
145163

146164
for(auto &state : hoa.body)
147-
if(!state.first.acc_sig.empty())
165+
if(state.first.is_accepting())
148166
{
149167
liveness_disjuncts.push_back(equal_exprt{
150168
buechi_state, from_integer(state.first.number, state_type)});
@@ -155,6 +173,37 @@ ltl_to_buechi(const exprt &property, message_handlert &message_handler)
155173
message.debug() << "Buechi liveness signal: " << format(liveness_signal)
156174
<< messaget::eom;
157175

176+
// construct the error signal -- true when the next automaton state
177+
// is nonaccepting with an unconditional self-loop.
178+
std::vector<exprt> error_disjuncts;
179+
180+
std::map<mp_integer, std::pair<hoat::state_namet, hoat::edgest>> state_map;
181+
for(auto &state : hoa.body)
182+
state_map[state.first.number] = state;
183+
184+
for(auto &state : hoa.body)
185+
{
186+
for(auto &edge : state.second)
187+
{
188+
if(edge.dest_states.size() != 1)
189+
throw ebmc_errort() << "edge must have one destination state";
190+
auto dest_state_it = state_map.find(edge.dest_states.front());
191+
CHECK_RETURN(dest_state_it != state_map.end());
192+
if(is_error_state(dest_state_it->second))
193+
{
194+
auto pre = equal_exprt{
195+
buechi_state, from_integer(state.first.number, state_type)};
196+
auto cond = hoa_label_to_expr(edge.label, ltl_sva_to_string);
197+
error_disjuncts.push_back(and_exprt{pre, cond});
198+
}
199+
}
200+
}
201+
202+
auto error_signal = disjunction(error_disjuncts);
203+
204+
message.debug() << "Buechi error signal: " << format(error_signal)
205+
<< messaget::eom;
206+
158207
// construct the transition relation
159208
std::vector<exprt> trans_disjuncts;
160209

@@ -183,6 +232,7 @@ ltl_to_buechi(const exprt &property, message_handlert &message_handler)
183232
buechi_state,
184233
std::move(init),
185234
std::move(trans),
235+
std::move(error_signal),
186236
std::move(liveness_signal)};
187237
}
188238
catch(ltl_sva_to_string_unsupportedt error)

src/temporal-logic/ltl_to_buechi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@ struct buechi_transt
1717

1818
exprt init;
1919
exprt trans;
20+
exprt error_signal;
2021
exprt liveness_signal;
2122

2223
buechi_transt(
2324
symbol_exprt __state_symbol,
2425
exprt __init,
2526
exprt __trans,
27+
exprt __error_signal,
2628
exprt __liveness_signal)
2729
: state_symbol(std::move(__state_symbol)),
2830
init(std::move(__init)),
2931
trans(std::move(__trans)),
32+
error_signal(std::move(__error_signal)),
3033
liveness_signal(std::move(__liveness_signal))
3134
{
3235
}

0 commit comments

Comments
 (0)