Theory Voblint_Analysis_Sign.Sign_Backward
theory Sign_Backward
imports
Sign_Arithmetic
"Voblint_Nonrelational.Exec_Backward"
"Voblint_Domain.Backward_Numeric_Queries"
begin
section ‹Sign backward filtering›
text ‹The sign meet lives with the lattice in \<^theory>‹Voblint_Domain.Sign_Lattice›.›
subsection ‹Backward-analysis: inverse operators›
text ‹
Per the backward-domain plan, @{text inv_less_sign} provides sign-specific
refinement when a guard @{text "e1 < e2"} is known true or false. Plus/minus/times
are too coarse for useful arithmetic inversion in sign, so they instantiate the
shared @{const inv_conservative} (identity) instead of a per-domain no-op; the
structural bfilter propagation (And/Or/Not/Eq) is where sign gains.
›
fun inv_less_sign :: "bool => sign => sign => sign * sign" where
"inv_less_sign True a1 a2 =
(let a1' = if sign_le a2 SNonPos then a1 ⊓ SNeg else a1 ;
a2' = if sign_le a1 SNonNeg then a2 ⊓ SPos else a2
in (a1', a2'))"
| "inv_less_sign False a1 a2 =
(let a1' = if sign_le a2 SPos then a1 ⊓ SPos
else if sign_le a2 SNonNeg then a1 ⊓ SNonNeg
else a1 ;
a2' = if sign_le a1 SNeg then a2 ⊓ SNeg
else if sign_le a1 SNonPos then a2 ⊓ SNonPos
else a2
in (a1', a2'))"
lemma inv_less_sign_sound:
"n1 ∈ gamma_sign a1 ⟹ n2 ∈ gamma_sign a2 ⟹ (n1 < n2) = res
⟹ n1 ∈ gamma_sign (fst (inv_less_sign res a1 a2))
∧ n2 ∈ gamma_sign (snd (inv_less_sign res a1 a2))"
by (cases res; cases a1; cases a2;
auto simp: less_eq_sign_def; linarith)
text ‹
@{text inv_eq_sign} narrows on a guard @{text ‹e1 = e2›} known true or
false. The true branch narrows both operands to their intersection, exactly
matching what ‹bfilter› already computes for @{text ‹Eq _ _ True›}
via @{const inf} directly. The false branch only narrows a boundary
singleton away from a wider operand that has @{const SZero} as one of its
bounds: excluding @{text 0} from @{const SNonNeg} leaves @{const SPos},
from @{const SNonPos} leaves @{const SNeg}; both directions, symmetric in
which operand is at most @{const SZero}. Two jointly-at-most-@{const SZero}
operands are jointly unreachable. Every other pair --- including any pair
where the excluded value would not sit at a representable lattice boundary
--- passes through unchanged: the seven-element lattice cannot express
\"nonzero\" or an interior exclusion as a single value.
The guard tests @{term ‹sign_le a SZero›} (the set @{term ‹{SBot, SZero}›}),
not literal equality @{term ‹a = SZero›}: @{const SBot} is strictly below
@{const SZero}, so an equality guard would let a wider input at @{const
SZero} trigger narrowing that a strictly more precise input at @{const
SBot} would not --- a genuine monotonicity failure, not merely a missed
precision opportunity. The order-based guard closes that gap the same way
@{const inv_less_sign}'s own @{term ‹sign_le a2 SNonPos›}-style guards do;
it also makes the @{const SBot} case sound for free (vacuously, since
@{term ‹gamma_sign SBot = {}›}), without @{const inv_less_sign}'s separate
reliance on @{text ‹SBot ⊓ _ = SBot›}.
›
fun inv_eq_sign :: "bool => sign => sign => sign * sign" where
"inv_eq_sign True a1 a2 = (a1 ⊓ a2, a1 ⊓ a2)"
| "inv_eq_sign False a1 a2 =
(let a1' = if sign_le a1 SZero ∧ sign_le a2 SZero then SBot
else if sign_le a2 SZero ∧ sign_le a1 SNonNeg then a1 ⊓ SPos
else if sign_le a2 SZero ∧ sign_le a1 SNonPos then a1 ⊓ SNeg
else a1 ;
a2' = if sign_le a1 SZero ∧ sign_le a2 SZero then SBot
else if sign_le a1 SZero ∧ sign_le a2 SNonNeg then a2 ⊓ SPos
else if sign_le a1 SZero ∧ sign_le a2 SNonPos then a2 ⊓ SNeg
else a2
in (a1', a2'))"
lemma inv_eq_sign_sound:
"n1 ∈ gamma_sign a1 ⟹ n2 ∈ gamma_sign a2 ⟹ (n1 = n2) = res
⟹ n1 ∈ gamma_sign (fst (inv_eq_sign res a1 a2))
∧ n2 ∈ gamma_sign (snd (inv_eq_sign res a1 a2))"
proof (cases res)
case True
assume h1: "n1 ∈ gamma_sign a1" and h2: "n2 ∈ gamma_sign a2"
and heq: "(n1 = n2) = res"
have "n1 = n2" using heq True by simp
then have "n1 ∈ gamma_sign a2" using h2 by simp
then have "n1 ∈ gamma_sign (a1 ⊓ a2)"
using inf_sign_sound[OF h1] by simp
then show ?thesis using True ‹n1 = n2› by simp
next
case False
assume h1: "n1 ∈ gamma_sign a1" and h2: "n2 ∈ gamma_sign a2"
and heq: "(n1 = n2) = res"
have hne: "n1 ≠ n2" using heq False by simp
show ?thesis
using h1 h2 hne False
by (cases a1; cases a2;
auto simp: less_eq_sign_def Let_def)
qed
lemma inv_eq_sign_mono:
assumes "a1 ≤ (a1' :: sign)"
and "a2 ≤ a2'"
shows
"le_pair (inv_eq_sign r a1 a2) (inv_eq_sign r a1' a2')"
proof (cases r)
case True
then show ?thesis using assms by (simp add: le_infI1 le_infI2)
next
case False
then show ?thesis
using assms unfolding less_eq_sign_def
by (cases a1; cases a1'; simp; cases a2; cases a2'; simp add: Let_def)
qed
text ‹
Monotonicity of @{const inv_less_sign} needs a case split on both the guard's
truth value and which side of the shared narrowing threshold each operand sits;
‹narrow1_mono›/‹narrow2_mono› factor the ‹if›-cascade shape common to both
branches so the case split is done once, generically in a
@{class semilattice_inf} operand, rather than twice inline.
›
lemma sign_le_iff:
"sign_le a b ⟷ a ≤ b"
by (cases a; cases b; auto simp: less_eq_sign_def)
lemma narrow1_mono:
fixes x x' y y' c d :: "'a::semilattice_inf"
assumes xx': "x ≤ x'"
and yy': "y ≤ y'"
shows
"(if y ≤ c then x ⊓ d else x)
≤ (if y' ≤ c then x' ⊓ d else x')"
using xx' yy' inf_mono
by auto
lemma narrow2_mono:
fixes x x' y y' c1 c2 :: "'a::semilattice_inf"
assumes xx': "x ≤ x'"
and yy': "y ≤ y'"
and cc': "c1 ≤ c2"
shows
"(if y ≤ c1 then x ⊓ c1
else if y ≤ c2 then x ⊓ c2
else x)
≤
(if y' ≤ c1 then x' ⊓ c1
else if y' ≤ c2 then x' ⊓ c2
else x')"
using xx' yy' cc' inf_mono
by (auto simp add: inf.coboundedI1 inf.coboundedI2)
lemma inv_less_sign_mono:
assumes A1: "a1 ≤ (a1' :: sign)"
and A2: "a2 ≤ a2'"
shows
"le_pair (inv_less_sign r a1 a2) (inv_less_sign r a1' a2')"
proof (cases r)
case True
have fst_mono:
"(if a2 ≤ SNonPos then a1 ⊓ SNeg else a1)
≤
(if a2' ≤ SNonPos then a1' ⊓ SNeg else a1')"
using narrow1_mono[OF A1 A2, of SNonPos SNeg] .
have snd_mono:
"(if a1 ≤ SNonNeg then a2 ⊓ SPos else a2)
≤
(if a1' ≤ SNonNeg then a2' ⊓ SPos else a2')"
using narrow1_mono[OF A2 A1, of SNonNeg SPos] .
show ?thesis
using True fst_mono snd_mono
by (simp only: inv_less_sign.simps Let_def sign_le_iff prod.sel)
next
case False
have pos_order: "(SPos :: sign) ≤ SNonNeg"
by (simp add: less_eq_sign_def)
have neg_order: "(SNeg :: sign) ≤ SNonPos"
by (simp add: less_eq_sign_def)
have fst_mono:
"(if a2 ≤ SPos then a1 ⊓ SPos
else if a2 ≤ SNonNeg then a1 ⊓ SNonNeg
else a1)
≤
(if a2' ≤ SPos then a1' ⊓ SPos
else if a2' ≤ SNonNeg then a1' ⊓ SNonNeg
else a1')"
using narrow2_mono[OF A1 A2, of SPos SNonNeg] pos_order by simp
have snd_mono:
"(if a1 ≤ SNeg then a2 ⊓ SNeg
else if a1 ≤ SNonPos then a2 ⊓ SNonPos
else a2)
≤
(if a1' ≤ SNeg then a2' ⊓ SNeg
else if a1' ≤ SNonPos then a2' ⊓ SNonPos
else a2')"
using narrow2_mono[OF A2 A1, of SNeg SNonPos] neg_order by simp
show ?thesis
using False fst_mono snd_mono
by (simp only: inv_less_sign.simps Let_def sign_le_iff prod.sel)
qed
subsection ‹The refinement operations and their certificate›
text ‹
Sign's refinement choices: the lattice meet, the sign-specific comparison
inverses, and the conservative identity for arithmetic. The guard filters and
the branch are not stated here; ‹Sign_Transfer› derives them from this record
and the evaluator.
›
definition sign_refine_ops :: "sign refine_ops" where
"sign_refine_ops =
⦇r_tobool = sign_tobool, r_inv_less = inv_less_sign, r_inv_eq = inv_eq_sign,
r_inv_plus = inv_conservative, r_inv_minus = inv_conservative,
r_inv_times = inv_conservative, r_intersect = inf⦈"
lemma sign_refine_ops_simps [simp]:
"r_tobool sign_refine_ops = sign_tobool"
"r_inv_less sign_refine_ops = inv_less_sign"
"r_inv_eq sign_refine_ops = inv_eq_sign"
"r_inv_plus sign_refine_ops = inv_conservative"
"r_inv_minus sign_refine_ops = inv_conservative"
"r_inv_times sign_refine_ops = inv_conservative"
"r_intersect sign_refine_ops = inf"
by (simp_all add: sign_refine_ops_def)
text ‹
The certificate discharges soundness, monotonicity, and reductiveness together
against @{locale mono_refinement}. Each ‹inv_*›'s mono/reductive obligation
is one @{const le_pair} fact, transparent notation for the componentwise ‹∧› the
per-operator lemmas above already prove.
›
lemma sign_backward_domain:
"mono_refinement inf aval_sign sign_tobool
inv_less_sign inv_eq_sign inv_conservative inv_conservative inv_conservative"
proof unfold_locales
qed (use sign_tobool_mono in ‹simp_all add: inf_sign_sound inv_less_sign_sound
inv_eq_sign_sound inv_conservative_def sign_tobool_sound inf_mono sign_arith.aval_dom_mono
inv_less_sign_mono inv_eq_sign_mono le_infI1 le_infI2›)
end