Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

what4: Don't annotate {Nonce,}AppExprs #247

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

langston-barrett
Copy link
Contributor

Fixes #246. Really, Annotation shouldn't need to carry a Nonce, because the outer NonceAppExpr will already have one. However, actually removing it is challenging due to the return type of sbNonceExpr, and in turn ExprAllocator's nonceExpr, which returns an Expr. Surely in practice, this is always a NonceAppExpr, but there's no way to tell from the type. To avoid a larger refactor or introducing partiality, I'm keeping the Nonce in Annotation for now.

Fixes #246, though we may want to create a follow-up about removing the Nonce from Annotation.

@langston-barrett langston-barrett self-assigned this Dec 6, 2023
@langston-barrett langston-barrett force-pushed the lb/ann-nonce-app branch 2 times, most recently from a3199cb to 1dd0545 Compare December 6, 2023 17:08
@langston-barrett
Copy link
Contributor Author

langston-barrett commented Dec 6, 2023

CI fails with:

test unsafeSetAbstractValue2:            FAIL
    test/ExprBuilderSMTLib2.hs:1157:
    unsafeSetAbstractValue doesn't work as expected for a
    compound symbolic expression

the test:

-- Test unsafeSetAbstractValue on a compound symbolic expression
testUnsafeSetAbstractValue2 :: TestTree
testUnsafeSetAbstractValue2 = testCase "test unsafeSetAbstractValue2" $
withSym FloatIEEERepr $ \sym -> do
let w = knownNat @8
e2A <- freshConstant sym (userSymbol' "x2A") (BaseBVRepr w)
e2B <- freshConstant sym (userSymbol' "x2B") (BaseBVRepr w)
e2C <- bvAdd sym e2A e2B
(_, e2C') <- annotateTerm sym $ unsafeSetAbstractValue (WUB.BVDArith (WUBA.range w 2 2)) e2C
unsignedBVBounds e2C' @?= Just (2, 2)
e2D <- bvAdd sym e2C' =<< bvLit sym w (BV.one w)
case asBV e2D of
Just bv -> bv @?= BV.mkBV w 3
Nothing -> assertFailure $ unlines
[ "unsafeSetAbstractValue doesn't work as expected for a"
, "compound symbolic expression"
]

I'm able to reproduce this locally with

cabal configure --enable-tests
cabal run test:expr-builder-smtlib2 -- -p 'unsafeSetAbstractValue2'

On this branch,

    print e2A
    print e2B
    print e2C
    print e2C'
    print e2D

yields

cx2A@0:bv
cx2B@1:bv
bvSum cx2B@1:bv cx2A@0:bv
bvSum cx2B@1:bv cx2A@0:bv
bvSum cx2B@1:bv cx2A@0:bv 0x1:[8]

whereas on master it yields

cx2A@0:bv
cx2B@1:bv
bvSum cx2B@1:bv cx2A@0:bv
annotation Nonce {indexValue = 3} (bvSum cx2B@1:bv cx2A@0:bv)
0x3:[8]

@langston-barrett
Copy link
Contributor Author

langston-barrett commented Dec 6, 2023

Printf-debugging shows that this branch hits this case in semiRingAdd:

(SR_Sum xs, SR_Constant yc) ->

whereas master hits the base case, likely because the "annotated" expression is (incorrectly) not recognized as a semiring sum:

_ -> semiRingSum sym (WSum.addVars sr x y)

Counter-intuitively, it appears that hitting the more specialized case results in a less-specific answer. Possibly, the WSum.addConstant code doesn't take abstract domains into account as carefully as WSum.addVars?

@langston-barrett
Copy link
Contributor Author

langston-barrett commented Dec 6, 2023

I think this may have to do with the fact that the catch-all case calls this function:

semiRingSum ::
ExprBuilder t st fs ->
WeightedSum (Expr t) sr ->
IO (Expr t (SR.SemiRingBase sr))
semiRingSum sym s
| Just c <- WSum.asConstant s = semiRingLit sym (WSum.sumRepr s) c
| Just r <- WSum.asVar s = return r
| otherwise = sum' sym s

whereas the more specific case calls sum' directly, bypassing WSum.asConstant. There are two possible solutions:

  1. Call semiRingSum ranther than sum' in the (SR_Sum, SR_Constant) case. This could be beneficial to performance if this case is often reducible to a constant, or detrimental if it's not. I'm not sure why sum' is called directly, perhaps it's the assumption that it's not common.
  2. Call WSum.asConstant in asBV. Again, this could possibly make a lot of other code faster or slower depending on how common the case of a constant-able sum appears.

[EDIT]: This does not appear to be the problem, adding semiRingSum to the above case doesn't fix the test case.

@langston-barrett
Copy link
Contributor Author

langston-barrett commented Dec 6, 2023

It appears that WSum.addVars (in master/the base case) is hitting this case of sbMakeExpr

BaseBVRepr w | Just x <- BVD.asSingleton v -> bvLit sym w (BV.mkBV w x)

whereas the result of WSum.addConstant (on this branch) hits the catch-all case:

_ -> appExpr s pc a v

So I need to figure out why abstractEval treats these differently.

To be more specific, the question is why BVD.asSingleton returns Nothing for bvSum cx2B@1:bv cx2A@0:bv 0x1:[8] while it returns Just for bvSum annotation Nonce {indexValue = 3} (bvSum cx2B@1:bv cx2A@0:bv) 0x1:[8].

@langston-barrett
Copy link
Contributor Author

To be more specific, the question is why BVD.asSingleton returns Nothing for bvSum cx2B@1:bv cx2A@0:bv 0x1:[8] while it returns Just for bvSum annotation Nonce {indexValue = 3} (bvSum cx2B@1:bv cx2A@0:bv) 0x1:[8].

FWIW,

         let e2C' = O.BVDArith (A.range w 2 2)
         let e2D = O.add e2C' (O.singleton w 1)
         case O.asSingleton e2D of
           Just bv -> pure () -- bv == mkBV w 3
           Nothing -> error "sad"

works just fine. Perhaps the abstract domain is getting lost when the binary bvAdd x2B x2A gets "lifted" into the ternary sum with 0x1, which (again, probably erroneously) doesn't happen when the binary sum is "hidden" under the annotation constructor?

@langston-barrett
Copy link
Contributor Author

Okay, I've managed to whittle down the issue to a test case, reported here (as it happens on master, not just on this branch): #248

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do {Nonce,}AppExprs need annotations?
1 participant