Skip to content

Commit 5f9a562

Browse files
authored
Merge pull request #1227 from diffblue/hoa-graph
clean up HOA accepting states
2 parents 8b6c449 + 1f76cac commit 5f9a562

File tree

5 files changed

+111
-1
lines changed

5 files changed

+111
-1
lines changed

src/temporal-logic/hoa.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,3 +543,40 @@ void hoat::output(std::ostream &out) const
543543

544544
out << "--END--\n";
545545
}
546+
547+
grapht<graph_nodet<hoat::graph_edget>> hoat::graph() const
548+
{
549+
grapht<graph_nodet<hoat::graph_edget>> graph;
550+
551+
graph.resize(numeric_cast_v<std::size_t>(max_state_number() + 1));
552+
553+
for(auto &state : body)
554+
{
555+
for(auto &edge : state.second)
556+
for(auto &dest : edge.dest_states)
557+
{
558+
graph.add_edge(state.first.number, dest);
559+
graph.edge(state.first.number, dest).label = edge.label;
560+
}
561+
}
562+
563+
return graph;
564+
}
565+
566+
void hoat::buechi_acceptance_cleanup()
567+
{
568+
using hoa_grapht = grapht<graph_nodet<hoat::graph_edget>>;
569+
auto as_graph = graph();
570+
for(auto &state : body)
571+
{
572+
if(state.first.is_accepting())
573+
{
574+
hoa_grapht::patht loop_path;
575+
as_graph.shortest_loop(state.first.number, loop_path);
576+
// when this state is not part of any cycle,
577+
// then we cannot have Buechi acceptance via this state
578+
if(loop_path.empty())
579+
state.first.acc_sig.clear();
580+
}
581+
}
582+
}

src/temporal-logic/hoa.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Author: Daniel Kroening, dkr@amazon.com
99
#ifndef CPROVER_TEMPORAL_LOGIC_HOA_H
1010
#define CPROVER_TEMPORAL_LOGIC_HOA_H
1111

12+
#include <util/graph.h>
1213
#include <util/irep.h>
1314

1415
#include <cstdint>
@@ -47,7 +48,7 @@ class hoat
4748
acc_sigt acc_sig; // acceptance sets
4849
};
4950
using edgest = std::list<edget>;
50-
using bodyt = std::list<std::pair<state_namet, edgest>>;
51+
using bodyt = std::vector<std::pair<state_namet, edgest>>;
5152
bodyt body;
5253

5354
hoat(headert _header, bodyt _body)
@@ -68,6 +69,19 @@ class hoat
6869

6970
// atomic propositions
7071
std::map<intt, std::string> ap_map;
72+
73+
// convert into a graph
74+
struct graph_edget
75+
{
76+
labelt label;
77+
};
78+
79+
grapht<graph_nodet<graph_edget>> graph() const;
80+
81+
// Remove accepting states that are not part of a cycle.
82+
// These are irrelevant when using the standard Buechi
83+
// acceptance criterion.
84+
void buechi_acceptance_cleanup();
7185
};
7286

7387
#endif // CPROVER_TEMPORAL_LOGIC_HOA_H

src/temporal-logic/ltl_to_buechi.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ ltl_to_buechi(const exprt &property, message_handlert &message_handler)
135135

136136
message.debug() << hoa << messaget::eom;
137137

138+
// clean up accepting states
139+
hoa.buechi_acceptance_cleanup();
140+
138141
auto max_state_number = hoa.max_state_number();
139142
auto state_type = range_typet{0, max_state_number};
140143
const auto buechi_state = symbol_exprt{"buechi::state", state_type};

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ SRC = unit_tests.cpp
55

66
# Test source files
77
SRC += smvlang/expr2smv.cpp \
8+
temporal-logic/hoa.cpp \
89
temporal-logic/ltl_sva_to_string.cpp \
910
temporal-logic/sva_sequence_match.cpp \
1011
temporal-logic/nnf.cpp \

unit/temporal-logic/hoa.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*******************************************************************\
2+
3+
Module: Hanoi Omega Automata (HOA) Format
4+
5+
Author: Daniel Kroening, dkr@amazon.com
6+
7+
\*******************************************************************/
8+
9+
#include <temporal-logic/hoa.h>
10+
#include <testing-utils/use_catch.h>
11+
12+
SCENARIO("Parsing a HOA from a string")
13+
{
14+
GIVEN("A HOA string for Xp")
15+
{
16+
auto hoa_string =
17+
"HOA: v1.1\n"
18+
"name: X!a0\n"
19+
"States: 4\n"
20+
"Start: 1\n"
21+
"AP: 1 a0\n"
22+
"acc-name: Buchi\n"
23+
"Acceptance: 1 Inf ( 0 )\n"
24+
"properties: trans-labels explicit-labels state-acc complete\n"
25+
"properties: deterministic terminal very-weak\n"
26+
"--BODY--\n"
27+
"State: 0 {0}\n"
28+
"[!0] 2\n"
29+
"[0] 3\n"
30+
"State: 1 {0}\n"
31+
"[t] 0\n"
32+
"State: 2 {0}\n"
33+
"[t] 2\n"
34+
"State: 3\n"
35+
"[t] 3\n"
36+
"--END--\n";
37+
38+
auto hoa = hoat::from_string(hoa_string);
39+
40+
REQUIRE(hoa.body.size() == 4);
41+
42+
REQUIRE(hoa.body[0].first.is_accepting());
43+
REQUIRE(hoa.body[1].first.is_accepting());
44+
REQUIRE(hoa.body[2].first.is_accepting());
45+
REQUIRE(!hoa.body[3].first.is_accepting());
46+
47+
// now remove the extra accepting states
48+
hoa.buechi_acceptance_cleanup();
49+
50+
REQUIRE(!hoa.body[0].first.is_accepting());
51+
REQUIRE(!hoa.body[1].first.is_accepting());
52+
REQUIRE(hoa.body[2].first.is_accepting());
53+
REQUIRE(!hoa.body[3].first.is_accepting());
54+
}
55+
}

0 commit comments

Comments
 (0)