Skip to content

Commit 6fe024a

Browse files
committed
LTL to CTL conversion
This adds conversion from a fragment of LTL to CTL, for the benefit of the BDD engine.
1 parent 8c5ac32 commit 6fe024a

File tree

5 files changed

+77
-20
lines changed

5 files changed

+77
-20
lines changed

regression/smv/smv/bdd_unsupported_property.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ bdd_unsupported_property.smv
33
--bdd
44
^EXIT=10$
55
^SIGNAL=0$
6-
^\[spec1\] !G x = FALSE: FAILURE: property not supported by BDD engine$
6+
^\[spec1\] G x \| F x: FAILURE: property not supported by BDD engine$
77
^\[spec2\] G x = FALSE: REFUTED$
88
--
99
^warning: ignoring

regression/smv/smv/bdd_unsupported_property.smv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ VAR x : boolean;
44

55
ASSIGN init(x) := TRUE;
66

7-
LTLSPEC !G x=FALSE
7+
LTLSPEC G x | F x
88
LTLSPEC G x=FALSE

src/ebmc/bdd_engine.cpp

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ bool bdd_enginet::property_supported(const exprt &expr)
458458
if(is_LTL(expr))
459459
{
460460
// We can map selected path properties to CTL.
461-
return is_Gp(expr) || is_GFp(expr);
461+
return LTL_to_CTL(expr).has_value();
462462
}
463463

464464
if(is_SVA(expr))
@@ -514,23 +514,6 @@ void bdd_enginet::check_property(propertyt &property)
514514
message.status() << "Checking " << property.name << messaget::eom;
515515
property.status=propertyt::statust::UNKNOWN;
516516

517-
// Our engine knows CTL only.
518-
// We map selected path properties to CTL.
519-
520-
if(is_Gp(property.normalized_expr))
521-
{
522-
// G p --> AG p
523-
auto p = to_G_expr(property.normalized_expr).op();
524-
property.normalized_expr = AG_exprt{p};
525-
}
526-
527-
if(is_GFp(property.normalized_expr))
528-
{
529-
// G F p --> AG AF p
530-
auto p = to_F_expr(to_G_expr(property.normalized_expr).op()).op();
531-
property.normalized_expr = AG_exprt{AF_exprt{p}};
532-
}
533-
534517
if(
535518
property.normalized_expr.id() == ID_sva_always &&
536519
to_sva_always_expr(property.normalized_expr).op().id() ==
@@ -555,6 +538,18 @@ void bdd_enginet::check_property(propertyt &property)
555538
property.normalized_expr = AG_exprt{p};
556539
}
557540

541+
// Our engine knows CTL only.
542+
// We map selected path properties to CTL.
543+
if(
544+
has_temporal_operator(property.normalized_expr) &&
545+
is_LTL(property.normalized_expr))
546+
{
547+
auto CTL_opt = LTL_to_CTL(property.normalized_expr);
548+
CHECK_RETURN(CTL_opt.has_value());
549+
property.normalized_expr = CTL_opt.value();
550+
CHECK_RETURN(is_CTL(property.normalized_expr));
551+
}
552+
558553
if(is_AGp(property.normalized_expr))
559554
{
560555
check_AGp(property);

src/temporal-logic/temporal_logic.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Author: Daniel Kroening, [email protected]
1212

1313
#include <verilog/sva_expr.h>
1414

15+
#include "ctl.h"
16+
#include "ltl.h"
17+
1518
bool is_temporal_operator(const exprt &expr)
1619
{
1720
return is_CTL_operator(expr) || is_LTL_operator(expr) ||
@@ -152,3 +155,58 @@ bool is_SVA_always_s_eventually_p(const exprt &expr)
152155
!has_temporal_operator(
153156
to_sva_s_eventually_expr(to_sva_always_expr(expr).op()).op());
154157
}
158+
159+
std::optional<exprt> LTL_to_CTL(exprt expr)
160+
{
161+
// We map a subset of LTL to ACTL, following
162+
// Monika Maidl. "The common fragment of CTL and LTL"
163+
// http://dx.doi.org/10.1109/SFCS.2000.892332
164+
//
165+
// Specificially, we allow
166+
// * state predicates
167+
// * conjunctions of allowed formulas
168+
// * X φ, where φ is allowed
169+
// * F φ, where φ is allowed
170+
// * G φ, where φ is allowed
171+
if(!has_temporal_operator(expr))
172+
{
173+
return expr;
174+
}
175+
else if(expr.id() == ID_and)
176+
{
177+
for(auto &op : expr.operands())
178+
{
179+
auto rec = LTL_to_CTL(op);
180+
if(!rec.has_value())
181+
return {};
182+
op = *rec;
183+
}
184+
return expr;
185+
}
186+
else if(expr.id() == ID_X)
187+
{
188+
auto rec = LTL_to_CTL(to_X_expr(expr).op());
189+
if(rec.has_value())
190+
return AX_exprt{*rec};
191+
else
192+
return {};
193+
}
194+
else if(expr.id() == ID_F)
195+
{
196+
auto rec = LTL_to_CTL(to_F_expr(expr).op());
197+
if(rec.has_value())
198+
return AF_exprt{*rec};
199+
else
200+
return {};
201+
}
202+
else if(expr.id() == ID_G)
203+
{
204+
auto rec = LTL_to_CTL(to_G_expr(expr).op());
205+
if(rec.has_value())
206+
return AG_exprt{*rec};
207+
else
208+
return {};
209+
}
210+
else
211+
return {};
212+
}

src/temporal-logic/temporal_logic.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,8 @@ bool is_SVA_always_p(const exprt &);
7777
/// Returns true iff the given expression is always s_eventually p
7878
bool is_SVA_always_s_eventually_p(const exprt &);
7979

80+
/// Turns some fragment of LTL into equivalent CTL, or
81+
/// returns {} if not possible
82+
std::optional<exprt> LTL_to_CTL(exprt);
83+
8084
#endif

0 commit comments

Comments
 (0)