Skip to content

Commit 04b5682

Browse files
authored
Merge pull request #744 from diffblue/property-nnf
extract per-node NNF computation for temporal logic from word-level BMC
2 parents 2d3101a + 75cf233 commit 04b5682

File tree

4 files changed

+130
-92
lines changed

4 files changed

+130
-92
lines changed

src/temporal-logic/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
SRC = negate_property.cpp \
2+
nnf.cpp \
23
normalize_property.cpp \
34
temporal_logic.cpp \
45
#empty line

src/temporal-logic/nnf.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*******************************************************************\
2+
3+
Module: Negation Normal Form for temporal logic
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include "nnf.h"
10+
11+
#include <verilog/sva_expr.h>
12+
13+
#include "temporal_expr.h"
14+
15+
std::optional<exprt> negate_property_node(const exprt &expr)
16+
{
17+
if(expr.id() == ID_U)
18+
{
19+
// ¬(φ U ψ) ≡ (¬φ R ¬ψ)
20+
auto &U = to_U_expr(expr);
21+
return R_exprt{not_exprt{U.lhs()}, not_exprt{U.rhs()}};
22+
}
23+
else if(expr.id() == ID_R)
24+
{
25+
// ¬(φ R ψ) ≡ (¬φ U ¬ψ)
26+
auto &R = to_R_expr(expr);
27+
return U_exprt{not_exprt{R.lhs()}, not_exprt{R.rhs()}};
28+
}
29+
else if(expr.id() == ID_G)
30+
{
31+
// ¬G φ ≡ F ¬φ
32+
auto &G = to_G_expr(expr);
33+
return F_exprt{not_exprt{G.op()}};
34+
}
35+
else if(expr.id() == ID_F)
36+
{
37+
// ¬F φ ≡ G ¬φ
38+
auto &F = to_F_expr(expr);
39+
return G_exprt{not_exprt{F.op()}};
40+
}
41+
else if(expr.id() == ID_X)
42+
{
43+
// ¬X φ ≡ X ¬φ
44+
auto &X = to_X_expr(expr);
45+
return X_exprt{not_exprt{X.op()}};
46+
}
47+
else if(expr.id() == ID_implies)
48+
{
49+
// ¬(a->b) ≡ a && ¬b
50+
auto &implies_expr = to_implies_expr(expr);
51+
return and_exprt{implies_expr.lhs(), not_exprt{implies_expr.rhs()}};
52+
}
53+
else if(expr.id() == ID_and)
54+
{
55+
auto operands = expr.operands();
56+
for(auto &op : operands)
57+
op = not_exprt{op};
58+
return or_exprt{std::move(operands)};
59+
}
60+
else if(expr.id() == ID_or)
61+
{
62+
auto operands = expr.operands();
63+
for(auto &op : operands)
64+
op = not_exprt{op};
65+
return and_exprt{std::move(operands)};
66+
}
67+
else if(expr.id() == ID_not)
68+
{
69+
return to_not_expr(expr).op();
70+
}
71+
else if(expr.id() == ID_sva_until)
72+
{
73+
// ¬(φ W ψ) ≡ (¬φ strongR ¬ψ)
74+
auto &W = to_sva_until_expr(expr);
75+
return strong_R_exprt{not_exprt{W.lhs()}, not_exprt{W.rhs()}};
76+
}
77+
else if(expr.id() == ID_sva_s_until)
78+
{
79+
// ¬(φ U ψ) ≡ (¬φ R ¬ψ)
80+
auto &U = to_sva_s_until_expr(expr);
81+
return R_exprt{not_exprt{U.lhs()}, not_exprt{U.rhs()}};
82+
}
83+
else if(expr.id() == ID_sva_until_with)
84+
{
85+
// ¬(φ R ψ) ≡ (¬φ U ¬ψ)
86+
// Note LHS and RHS are swapped.
87+
auto &until_with = to_sva_until_with_expr(expr);
88+
auto R = R_exprt{until_with.rhs(), until_with.lhs()};
89+
return sva_until_exprt{not_exprt{R.lhs()}, not_exprt{R.rhs()}};
90+
}
91+
else if(expr.id() == ID_sva_s_until_with)
92+
{
93+
// ¬(φ strongR ψ) ≡ (¬φ W ¬ψ)
94+
// Note LHS and RHS are swapped.
95+
auto &s_until_with = to_sva_s_until_with_expr(expr);
96+
auto strong_R = strong_R_exprt{s_until_with.rhs(), s_until_with.lhs()};
97+
return weak_U_exprt{not_exprt{strong_R.lhs()}, not_exprt{strong_R.rhs()}};
98+
}
99+
else
100+
return {};
101+
}

src/temporal-logic/nnf.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*******************************************************************\
2+
3+
Module: Negation Normal Form for temporal logic
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_TEMPORAL_LOGIC_NNF_H
10+
#define CPROVER_TEMPORAL_LOGIC_NNF_H
11+
12+
#include <util/expr.h>
13+
14+
/// Negates a single node (i.e., not recursively) of a temporal logic formula.
15+
/// Returns {} if no negation is known.
16+
std::optional<exprt> negate_property_node(const exprt &);
17+
18+
#endif

src/trans-word-level/property.cpp

Lines changed: 10 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Author: Daniel Kroening, [email protected]
1717
#include <util/symbol_table.h>
1818

1919
#include <ebmc/ebmc_error.h>
20+
#include <temporal-logic/nnf.h>
2021
#include <temporal-logic/temporal_expr.h>
2122
#include <temporal-logic/temporal_logic.h>
2223
#include <verilog/sva_expr.h>
@@ -511,106 +512,23 @@ static obligationst property_obligations_rec(
511512
// We need NNF, try to eliminate the negation.
512513
auto &op = to_not_expr(property_expr).op();
513514

514-
if(op.id() == ID_U)
515-
{
516-
// ¬(φ U ψ) ≡ (¬φ R ¬ψ)
517-
auto &U = to_U_expr(op);
518-
auto R = R_exprt{not_exprt{U.lhs()}, not_exprt{U.rhs()}};
519-
return property_obligations_rec(R, current, no_timeframes);
520-
}
521-
else if(op.id() == ID_R)
522-
{
523-
// ¬(φ R ψ) ≡ (¬φ U ¬ψ)
524-
auto &R = to_R_expr(op);
525-
auto U = U_exprt{not_exprt{R.lhs()}, not_exprt{R.rhs()}};
526-
return property_obligations_rec(U, current, no_timeframes);
527-
}
528-
else if(op.id() == ID_G)
529-
{
530-
// ¬G φ ≡ F ¬φ
531-
auto &G = to_G_expr(op);
532-
auto F = F_exprt{not_exprt{G.op()}};
533-
return property_obligations_rec(F, current, no_timeframes);
534-
}
535-
else if(op.id() == ID_F)
536-
{
537-
// ¬F φ ≡ G ¬φ
538-
auto &F = to_F_expr(op);
539-
auto G = G_exprt{not_exprt{F.op()}};
540-
return property_obligations_rec(G, current, no_timeframes);
541-
}
542-
else if(op.id() == ID_X)
543-
{
544-
// ¬X φ ≡ X ¬φ
545-
auto &X = to_X_expr(op);
546-
auto negX = X_exprt{not_exprt{X.op()}};
547-
return property_obligations_rec(negX, current, no_timeframes);
548-
}
549-
else if(op.id() == ID_implies)
550-
{
551-
// ¬(a->b) --> a && ¬b
552-
auto &implies_expr = to_implies_expr(op);
553-
auto and_expr =
554-
and_exprt{implies_expr.lhs(), not_exprt{implies_expr.rhs()}};
555-
return property_obligations_rec(and_expr, current, no_timeframes);
556-
}
557-
else if(op.id() == ID_and)
558-
{
559-
auto operands = op.operands();
560-
for(auto &op : operands)
561-
op = not_exprt{op};
562-
auto or_expr = or_exprt{std::move(operands)};
563-
return property_obligations_rec(or_expr, current, no_timeframes);
564-
}
565-
else if(op.id() == ID_or)
566-
{
567-
auto operands = op.operands();
568-
for(auto &op : operands)
569-
op = not_exprt{op};
570-
auto and_expr = and_exprt{std::move(operands)};
571-
return property_obligations_rec(and_expr, current, no_timeframes);
572-
}
573-
else if(op.id() == ID_not)
515+
auto op_negated_opt = negate_property_node(op);
516+
517+
if(op_negated_opt.has_value())
574518
{
575519
return property_obligations_rec(
576-
to_not_expr(op).op(), current, no_timeframes);
577-
}
578-
else if(op.id() == ID_sva_until)
579-
{
580-
// ¬(φ W ψ) ≡ (¬φ strongR ¬ψ)
581-
auto &W = to_sva_until_expr(op);
582-
auto strong_R = strong_R_exprt{not_exprt{W.lhs()}, not_exprt{W.rhs()}};
583-
return property_obligations_rec(strong_R, current, no_timeframes);
520+
op_negated_opt.value(), current, no_timeframes);
584521
}
585-
else if(op.id() == ID_sva_s_until)
522+
else if(is_temporal_operator(op))
586523
{
587-
// ¬(φ U ψ) ≡ (¬φ R ¬ψ)
588-
auto &U = to_sva_s_until_expr(op);
589-
auto R = R_exprt{not_exprt{U.lhs()}, not_exprt{U.rhs()}};
590-
return property_obligations_rec(R, current, no_timeframes);
591-
}
592-
else if(op.id() == ID_sva_until_with)
593-
{
594-
// ¬(φ R ψ) ≡ (¬φ U ¬ψ)
595-
// Note LHS and RHS are swapped.
596-
auto &until_with = to_sva_until_with_expr(op);
597-
auto R = R_exprt{until_with.rhs(), until_with.lhs()};
598-
auto U = sva_until_exprt{not_exprt{R.lhs()}, not_exprt{R.rhs()}};
599-
return property_obligations_rec(U, current, no_timeframes);
600-
}
601-
else if(op.id() == ID_sva_s_until_with)
602-
{
603-
// ¬(φ strongR ψ) ≡ (¬φ W ¬ψ)
604-
// Note LHS and RHS are swapped.
605-
auto &s_until_with = to_sva_s_until_with_expr(op);
606-
auto strong_R = strong_R_exprt{s_until_with.rhs(), s_until_with.lhs()};
607-
auto W =
608-
weak_U_exprt{not_exprt{strong_R.lhs()}, not_exprt{strong_R.rhs()}};
609-
return property_obligations_rec(W, current, no_timeframes);
524+
throw ebmc_errort() << "failed to make NNF for " << op.id();
610525
}
611526
else
527+
{
528+
// state formula
612529
return obligationst{
613530
instantiate_property(property_expr, current, no_timeframes)};
531+
}
614532
}
615533
else
616534
{

0 commit comments

Comments
 (0)