зеркало из https://github.com/microsoft/mcBV.git
420 строки
16 KiB
Forth
420 строки
16 KiB
Forth
module RLEBVTheory
|
|
|
|
open Microsoft.Z3
|
|
open GlobalOptions
|
|
open Util
|
|
open Literal
|
|
open BooleanValuation
|
|
open Clause
|
|
open BitVectorValuation
|
|
open NumeralDB
|
|
open TheoryDB
|
|
open Trail
|
|
open State
|
|
open TheoryRelation
|
|
open BitVector
|
|
open Learning
|
|
open Z3Check
|
|
open RLEBVOperations
|
|
|
|
|
|
let tHolds (r:TheoryRelation) (pVal:Ref<BitVectorValuation>) (pNumerals:Ref<NumeralDB>) =
|
|
//assert (r.numUnpropagatedArgs pVal = 0)
|
|
|
|
let x_is_constant = if r.isSimpleRelation then r.isArgumentNumeral 0 else false
|
|
let y_is_constant = r.isRhsNumeral
|
|
|
|
let relOp =
|
|
(fun (x:BitVector) (y:BitVector) ->
|
|
match r.getRelationOP with
|
|
| Z3_decl_kind.Z3_OP_EQ when x.isConcreteValue && y.isConcreteValue -> if BitVector.bvEQ x y then True else False
|
|
| Z3_decl_kind.Z3_OP_ULEQ when x.isConcreteValue && y.isConcreteValue -> if BitVector.bvULEQ x y then True else False
|
|
| _ ->
|
|
|
|
match r.getRelationOP with
|
|
| Z3_decl_kind.Z3_OP_EQ ->
|
|
let intersection = (BitVector.Intersect x y)
|
|
let empty = not intersection.isValid
|
|
let x_is_subset_of_y = lazy ( BitVector.bvEQ x intersection)
|
|
let y_is_subset_of_x = lazy (BitVector.bvEQ y intersection)
|
|
|
|
if empty then False
|
|
elif x_is_constant && y_is_constant then
|
|
if BitVector.bvEQ x y then True else False
|
|
elif x_is_subset_of_y.Force() && y_is_constant ||
|
|
y_is_subset_of_x.Force() && x_is_constant then
|
|
True
|
|
else Undefined
|
|
| Z3_decl_kind.Z3_OP_ULEQ -> let x_min = x.Minimum
|
|
let x_max = x.Maximum
|
|
let y_min = y.Minimum
|
|
let y_max = y.Maximum
|
|
if BitVector.bvULEQ x_max y_min then True
|
|
elif not (BitVector.bvULEQ x_min y_max) then False
|
|
elif x_is_constant && y_is_constant then
|
|
assert(false) // No 1:num = 2:num
|
|
Undefined
|
|
else Undefined
|
|
| _ -> assert (false)
|
|
Undefined) // Error
|
|
|
|
let rhs = getRhsValue r pVal pNumerals
|
|
let lhs = getLhsValue r pVal pNumerals
|
|
assert(rhs.Length = lhs.Length)
|
|
|
|
relOp lhs rhs
|
|
|
|
|
|
let tGetAntecedents (s:State) (tRel:Ref<TheoryRelation>) : Literal list =
|
|
let MAexpls = (!s.bvVal).getMAssignmentExplRef
|
|
|
|
// Note: BV variables occurring in PA constraints can have
|
|
// the same PA constraint's boolVar as a reason (e.g., when they
|
|
// appear on decision level 0).
|
|
// CMW: Not completely sure this is true in the RLE theory.
|
|
|
|
let boolVar = (!tRel).getBoolVar
|
|
let mutable t = []
|
|
for i in 0 .. (!tRel).numArguments do
|
|
if not ((!tRel).isArgumentNumeral i) then
|
|
let e = (!MAexpls).[(!tRel).getArgument i]
|
|
let lit = (Negate e)
|
|
if lit <> 0 && e <> boolVar && not (List.exists (fun x -> x = lit) t) then
|
|
t <- lit :: t
|
|
t
|
|
|
|
|
|
let tGetImplication (s:State) (tRel:Ref<TheoryRelation>) (holds:Val) : (Clause * Literal) =
|
|
// Whenever this function is called, we know that tHolds is True or False.
|
|
assert (holds <> Undefined)
|
|
|
|
let boolVar = (!tRel).getBoolVar
|
|
let ants = (tGetAntecedents s tRel)
|
|
let lit = if holds = True then var2lit boolVar else Negate boolVar
|
|
let cl = newClauseFromList (lit :: ants)
|
|
checkExplanation s.trail s.database s.bvVal s.bounds cl false false true
|
|
(cl, lit)
|
|
|
|
|
|
let tGeneralize (s:State) (expl:Clause) : Clause =
|
|
// TODO: RLE-encoded conflict generalization
|
|
expl
|
|
|
|
|
|
//let tCheckConsistency (s:State) (wRel:Ref<TheoryRelation>) =
|
|
// let holds = tHolds (!wRel) s.partAssignment s.numeralDB
|
|
// let valB = (!s.bVal).getValueB (!wRel).getBoolVar
|
|
// match (holds, valB) with
|
|
// | (True,False)
|
|
// | (False,True) ->
|
|
// let cnflctCls = tExplainConflict s wRel
|
|
// s.conflict <- Some (ref cnflctCls)
|
|
// | _ -> ()
|
|
|
|
|
|
let tImplyNewValue (s:State) (antecedants:Literal list option) (bvVar:Var) (newValue:BitVector) =
|
|
// We have:
|
|
// oldReason => oldValue
|
|
// impAntecedents => newValue
|
|
// If oldValue /\ newValue = False then
|
|
// Conflict: not oldReason \/ not (impAntecedents)
|
|
// else
|
|
// oldReason /\ impAntecedents => newMAPredicate
|
|
|
|
let oldValue = (!s.bvVal).getValue bvVar
|
|
let newRel = getModelAssignmentPredicate s.database bvVar newValue s.bVal s.bvVal s.bounds
|
|
let b = newRel.getBoolVar
|
|
let is_more_concrete = newValue.isMoreConcreteThan oldValue
|
|
|
|
// Theoretically, b could already be true, because we may
|
|
// be seeing the same predicate a second time. b = False means
|
|
// that there is an old partial assignment for one part of the relation
|
|
// and the new partial assignment for the other part is conflicting.
|
|
|
|
match (!s.bVal).getValueB (b) with
|
|
| True -> ()
|
|
| Undefined when is_more_concrete ->
|
|
|
|
match antecedants with
|
|
| Some(a) ->
|
|
let imp = (newClauseFromList (b :: a))
|
|
checkExplanation s.trail s.database s.bvVal s.bounds imp false false true
|
|
s.Push (Imp (ref imp, b))
|
|
| None -> s.Push (BoolDecision b)
|
|
|
|
assert (tHolds newRel (s.bvVal) (s.numeralDB) <> Undefined || s.IsConflicted)
|
|
|
|
| Undefined -> // when not is_more_concrete
|
|
assert(false)
|
|
| False ->
|
|
// AZ: This could yield a conflict, if the predicate b
|
|
// is not new, it can be negated on the trail already.
|
|
match antecedants with
|
|
| Some(a) -> s.SetConflict (Some (ref (newClauseFromList (b :: a))))
|
|
| None -> UNREACHABLE("tImplyNewValue called for a decision.")
|
|
()
|
|
|
|
|
|
let tGetImpliedPartialAssignment (s:State) (tRel:Ref<TheoryRelation>) =
|
|
assert((!s.bVal).getValueB (!tRel).getBoolVar = True)
|
|
let (bvVar, newValue) = tGetNewValues (!tRel) s.bvVal s.numeralDB
|
|
|
|
let boolVar = (!tRel).getBoolVar
|
|
let oldValue = if bvVar <> 0 then ((!s.bvVal).getValue bvVar) else newValue
|
|
let expl = Negate boolVar :: (tGetAntecedents s tRel)
|
|
|
|
if (bvVar = 0 || (newValue.isValid && (BitVector.bvEQ newValue oldValue))) then
|
|
()
|
|
elif newValue.isInvalid then
|
|
verbose <| (lazy (sprintf " | ==> Domain of %s:bv is empty; %s => False" (bvVar.ToString()) (clauseToString (newClauseFromList expl))))
|
|
|
|
let cc = newClauseFromList (expl)
|
|
s.SetConflict (Some (ref cc))
|
|
else
|
|
tImplyNewValue s (Some expl) bvVar newValue
|
|
|
|
|
|
let tEvaluateConcat (s:State) (tRel:Ref<TheoryRelation>) =
|
|
assert (numUnpropagatedVariables (!tRel) s.bvVal <= 2)
|
|
let watchedRel = !tRel
|
|
let pPAs = s.bvVal
|
|
let pNums = s.numeralDB
|
|
let MAexpls = (!s.bvVal).mAssignmntsExplanations
|
|
|
|
let mutable argValList = getArgValuePairs watchedRel pPAs pNums
|
|
assert (List.length argValList = 2)
|
|
|
|
// (concat a0 a1) <-> rhs
|
|
|
|
let (a0, v0) = argValList.Head
|
|
let (a1, v1) = argValList.Tail.Head
|
|
let (rhs, vr) = (watchedRel.getRhs, getRhsValue watchedRel pPAs pNums)
|
|
|
|
let nr = BitVector.bvConcat v0 v1
|
|
let nvr = BitVector.Intersect vr nr
|
|
let rr = if watchedRel.isRhsNumeral || MAexpls.[rhs] = 0 then [] else [ - MAexpls.[rhs]]
|
|
|
|
let na0 = BitVector.bvExtract (vr.Length - 1) (vr.Length - v0.Length) vr
|
|
let nv0 = BitVector.Intersect v0 na0
|
|
let r0 = if watchedRel.isArgumentNumeral 0 || MAexpls.[a0] = 0 then [] else [ - MAexpls.[watchedRel.getArgument 0]]
|
|
|
|
let na1 = BitVector.bvExtract (v1.Length - 1) 0 vr
|
|
let nv1 = BitVector.Intersect v1 na1
|
|
let r1 = if watchedRel.isArgumentNumeral 1 || MAexpls.[a1] = 0 then [] else [ - MAexpls.[watchedRel.getArgument 1]]
|
|
|
|
let mutable rhs_expl = []
|
|
for lst in [ r0; r1; rr ] do
|
|
for l in lst do
|
|
if l <> 0 && not (List.exists (fun x -> x = l) rhs_expl) then
|
|
rhs_expl <- l :: rhs_expl
|
|
|
|
for (newVar, oldValue, newValue, rsns) in [ (rhs, vr, nvr, rhs_expl);
|
|
(a0, v0, nv0, r0 @ rr);
|
|
(a1, v1, nv1, r1 @ rr) ] do
|
|
if not s.IsConflicted then
|
|
let impl = (Negate watchedRel.getBoolVar) :: rsns
|
|
if newValue.isValid then
|
|
assert(nr.isValid)
|
|
|
|
if not (BitVector.bvEQ newValue oldValue) then
|
|
// We have a new implication:
|
|
tImplyNewValue s (Some impl) newVar newValue
|
|
else
|
|
// intersect is not valid, so build an explanation (= conflict clause)
|
|
// oldReason /\ concat /\ reasons ==> False
|
|
// concat /\ reasons ==> - oldReason (trivial rewriting of ==>)
|
|
let cc = newClauseFromList impl
|
|
s.SetConflict (Some (ref cc))
|
|
|
|
|
|
let tEvaluateDiv0 (s:State) (tRel:Ref<TheoryRelation>) =
|
|
assert (numUnpropagatedVariables (!tRel) s.bvVal <= 2)
|
|
assert ((!tRel).numArguments = 2)
|
|
let watchedRel = !tRel
|
|
|
|
let maExpl = (!s.bvVal).getMAssignmentExplRef
|
|
let pPAs = s.bvVal
|
|
let pNums = s.numeralDB
|
|
let boolVar = watchedRel.getBoolVar
|
|
|
|
let bvVar = watchedRel.getRhs
|
|
let oldValue = getRhsValue watchedRel pPAs pNums
|
|
let rhsExpl = (!maExpl).[bvVar]
|
|
let width = oldValue.Length
|
|
|
|
let a0 = watchedRel.getArgument 0 // always there?
|
|
let v0 = getArgumentValue watchedRel pPAs pNums 0
|
|
|
|
let a1 = watchedRel.getArgument 1
|
|
let v1 = getArgumentValue watchedRel pPAs pNums 1
|
|
assert(BitVector.isAllZero v1) // is a udiv/0 or sdiv/0.
|
|
|
|
|
|
let mutable reasons = (Negate watchedRel.getBoolVar) :: (tGetAntecedents s (ref watchedRel))
|
|
|
|
let res = BitVector width
|
|
match watchedRel.getBVOP with
|
|
| Z3_decl_kind.Z3_OP_BUDIV ->
|
|
// The "hardware interpretation" for (bvudiv x 0) is #xffff
|
|
res.Bits <- [(width, Bit.One)]
|
|
|
|
let v0_rsn = if watchedRel.isArgumentNumeral 0 then 0 else (!maExpl).[a0]
|
|
reasons <- List.filter (fun x -> x <> v0_rsn) reasons
|
|
|
|
| Z3_decl_kind.Z3_OP_BSDIV when (snd v0.Bits.Head) <> Bit.U ->
|
|
// The "hardware interpretation" for (bvsdiv x 0) is (ite (bvslt x #x0000) #x0001 #xffff)
|
|
match v0.Bits.Head with
|
|
| (_, Bit.One) -> res.Bits <- [(width-1, Bit.Zero); (1, Bit.One)]
|
|
| (_, Bit.Zero) -> res.Bits <- [(width, Bit.One)]
|
|
| _ -> UNREACHABLE("unexpected bit")
|
|
| Z3_decl_kind.Z3_OP_BSDIV ->
|
|
// Sign not determined yet.
|
|
res.Length <- 0
|
|
res.Bits <- [(0, Bit.U)]
|
|
| _ -> UNREACHABLE("not a divison op")
|
|
|
|
if res.isValid then
|
|
let newValue = BitVector.Intersect oldValue res
|
|
if newValue.isValid then
|
|
assert(oldValue.isValid)
|
|
|
|
if not (BitVector.bvEQ newValue oldValue) then
|
|
// We have a new implication:
|
|
tImplyNewValue s (Some reasons) bvVar newValue
|
|
else
|
|
// newValue is not valid, so build an explanation (= conflict clause)
|
|
// oldReason /\ concat /\ reasons ==> False
|
|
// concat /\ reasons ==> - oldReason (trivial rewriting of ==>)
|
|
let cc = newClauseFromList reasons
|
|
s.SetConflict (Some (ref cc))
|
|
|
|
|
|
let tEvaluate0U (s:State) (tRel:Ref<TheoryRelation>) =
|
|
assert(numUnpropagatedVariables (!tRel) s.bvVal = 0)
|
|
|
|
// The TRel can be fully evaluated and checked against the trail.
|
|
// If it agrees with the trail, it's valueT is set
|
|
// If it contradicts the trail, a conflict is detected.
|
|
// If it is not present on the trail, it is propagated.
|
|
let pVal = s.bVal
|
|
let mAsgnmt = s.bvVal
|
|
let numerals = s.numeralDB
|
|
let watchedRel = !tRel
|
|
let boolVar = watchedRel.getBoolVar
|
|
|
|
let holds = (tHolds watchedRel mAsgnmt numerals)
|
|
let bval = ((!pVal).getValueB boolVar)
|
|
match (holds, bval) with
|
|
|
|
| (True, True) -> (!pVal).setValueT boolVar (!s.trail).getNumDecisions |> ignore
|
|
| (False, False) -> (!pVal).setValueT (Negate boolVar) (!s.trail).getNumDecisions |> ignore
|
|
|
|
| (True, False)
|
|
| (False, True) ->
|
|
let (expl, l) = tGetImplication s tRel holds
|
|
s.SetConflict (Some (ref expl))
|
|
|
|
| (True, Undefined)
|
|
| (False, Undefined) ->
|
|
let (expl, l) = tGetImplication s tRel holds
|
|
s.Push (Imp (ref expl, l))
|
|
|
|
| (Undefined, _) -> assert(false)
|
|
|
|
|
|
let tEvaluate1U (s:State) (tRel:Ref<TheoryRelation>) =
|
|
assert(numUnpropagatedVariables (!tRel) s.bvVal = 1)
|
|
// assert((!wRel).holds s.partAssignment s.numeralDB = Undefined)
|
|
// CMW: Not true, can hold even with unpropagated arguments (e.g., extract .. x = num)
|
|
|
|
let mas = s.bvVal
|
|
let nums = s.numeralDB
|
|
let pVal = s.bVal
|
|
let rel = !tRel
|
|
let boolVar = rel.getBoolVar
|
|
|
|
match rel.getRelationOP with
|
|
| Z3_decl_kind.Z3_OP_EQ ->
|
|
let bVal = (!pVal).getValueB boolVar
|
|
match bVal with
|
|
| True -> match rel.getBVOP with
|
|
| Z3_decl_kind.Z3_OP_CONCAT -> tEvaluateConcat s tRel
|
|
| Z3_decl_kind.Z3_OP_BUDIV
|
|
| Z3_decl_kind.Z3_OP_BSDIV
|
|
when (BitVector.isAllZero (getArgumentValue rel mas nums 1)) ->
|
|
tEvaluateDiv0 s tRel
|
|
| _ -> tGetImpliedPartialAssignment s tRel
|
|
| False -> ()
|
|
// Note 1: Could be a conflict, which is checked below.
|
|
// Note 2: Theoretically there could be only one value
|
|
// for the unpropagated argument, but it seems too expensive
|
|
// to get that here.
|
|
| _ ->
|
|
let holds = tHolds (!tRel) s.bvVal s.numeralDB
|
|
if holds <> Undefined then
|
|
let (expl, l) = tGetImplication s tRel holds
|
|
s.Push (Imp (ref expl, l))
|
|
|
|
| Z3_decl_kind.Z3_OP_ULEQ ->
|
|
let bVal = (!pVal).getValueB boolVar
|
|
let holds = tHolds (!tRel) s.bvVal s.numeralDB
|
|
match bVal with
|
|
| True ->
|
|
if holds = False then
|
|
let c = Negate (!tRel).getBoolVar :: (tGetAntecedents s tRel)
|
|
s.SetConflict (Some (ref (newClauseFromList c)))
|
|
elif holds = True then
|
|
(!s.bVal).setValueT boolVar (!s.trail).getNumDecisions |> ignore
|
|
| False ->
|
|
if holds = True then
|
|
let c = (!tRel).getBoolVar :: (tGetAntecedents s tRel)
|
|
s.SetConflict (Some (ref (newClauseFromList c)))
|
|
elif holds = False then
|
|
(!s.bVal).setValueT (Negate boolVar) (!s.trail).getNumDecisions |> ignore
|
|
| _ ->
|
|
if holds <> Undefined then
|
|
let (expl, l) = tGetImplication s tRel holds
|
|
s.Push (Imp (ref expl, l))
|
|
//assert(not s.IsConflicted) AZ: This is not guaranteed. See the similar assertion in BDecide.
|
|
|
|
| _ -> assert(false)
|
|
|
|
// CMW: This would be an optimization.
|
|
// tCheckConsistency s tRel
|
|
|
|
|
|
let tEvaluate2U (s:State) (tRel:Ref<TheoryRelation>) =
|
|
assert(numUnpropagatedVariables (!tRel) s.bvVal = 2)
|
|
|
|
let rel = !tRel
|
|
let mas = s.bvVal
|
|
let nums = s.numeralDB
|
|
|
|
match rel.getRelationOP with
|
|
| Z3_decl_kind.Z3_OP_EQ ->
|
|
match rel.getBVOP with
|
|
| Z3_decl_kind.Z3_OP_CONCAT -> tEvaluateConcat s tRel
|
|
| Z3_decl_kind.Z3_OP_BUDIV
|
|
| Z3_decl_kind.Z3_OP_BSDIV
|
|
when (BitVector.isAllZero (getArgumentValue rel mas nums 1)) ->
|
|
tEvaluateDiv0 s tRel
|
|
| _ -> ()
|
|
|
|
// CMW: This would be an optimization.
|
|
// tCheckConsistency s tRel
|
|
|
|
| Z3_decl_kind.Z3_OP_ULEQ ->
|
|
()
|
|
|
|
| _ -> assert(false)
|
|
|
|
|
|
let tEvaluate (s:State) (tRel:Ref<TheoryRelation>) =
|
|
let mAsgnmt = s.bvVal
|
|
|
|
match (numUnpropagatedVariables !tRel mAsgnmt) with
|
|
| 0 -> tEvaluate0U s tRel
|
|
| 1 -> tEvaluate1U s tRel
|
|
| 2 -> tEvaluate2U s tRel
|
|
| _ -> () // Nothing new to learn.
|