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

feat: add BitVec multiplication simp lemmas #6718

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

Conversation

vlad902
Copy link
Contributor

@vlad902 vlad902 commented Jan 20, 2025

This PR adds BitVec lemmas required to cancel multiplicative negatives, and plumb support through to bv_normalize to make use of this result in the normalized twos-complement form.

I include some bmod lemmas I found useful to prove this result, the two helper lemmas I add use the same naming/proofs as their emod equivalents.

This PR adds BitVec lemmas required to cancel multiplicative negatives,
and plumb support through to bv_normalize to make use of this result in
the normalized twos-complement form.

I include some bmod lemmas I found useful to prove this result, the two
helper lemmas I add use the same naming/proofs as their emod
equivalents.
@@ -88,6 +88,14 @@ example {x : BitVec 16} : x / (BitVec.ofNat 16 8) = x >>> 3 := by bv_normalize
example {x y : Bool} (h1 : x && y) : x || y := by bv_normalize
example (a b c: Bool) : (if a then b else c) = (if !a then c else b) := by bv_normalize

-- neg_mul' / mul_neg'
example (x y : BitVec 16) : (-x) * y = -(x * y) := by bv_normalize
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seemed useful to me to have both the normalized and unnormalized forms, but happy to reduce this to just the normalized if you prefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine but please also add test cases for the symmetric variants mentioned above.

@vlad902
Copy link
Contributor Author

vlad902 commented Jan 20, 2025

This is rows 181/182 of the spreadsheet.

@github-actions github-actions bot added the toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN label Jan 20, 2025
@leanprover-community-bot
Copy link
Collaborator

Mathlib CI status (docs):

  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase 1d03cd6a6b9e2cab387692f5ec0d0013b9a9867d --onto ac6a29ee834ba7fd30e9372e51493d7741e9c657. (2025-01-20 22:10:39)

Comment on lines +1197 to +1205
theorem add_bmod_eq_add_bmod_right (i : Int)
(H : bmod x n = bmod y n) : bmod (x + i) n = bmod (y + i) n := by
rw [← bmod_add_bmod_congr, ← @bmod_add_bmod_congr y, H]

theorem bmod_add_cancel_right (i : Int) : bmod (x + i) n = bmod (y + i) n ↔ bmod x n = bmod y n :=
fun H => by
have := add_bmod_eq_add_bmod_right (-i) H
rwa [Int.add_neg_cancel_right, Int.add_neg_cancel_right] at this,
fun H => by rw [← bmod_add_bmod_congr, H, bmod_add_bmod_congr]⟩
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't checked, but are there analogues of these for the other *mod variants? I would like to try to keep the discrepancy between the versions monotonically decreasing if possible. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as I mention in the commit message, these two are direct copies of their emod equivalents in both name and proof. bmod_neg_bmod is new as there isn't

@@ -257,6 +257,14 @@ theorem BitVec.add_const_right' (a b c : BitVec w) : (a + b) + c = (b + c) + a :
attribute [bv_normalize] BitVec.mul_zero
attribute [bv_normalize] BitVec.zero_mul

@[bv_normalize]
theorem BitVec.neg_mul' (x y : BitVec w) : (~~~x + 1#w) * y = ~~~(x * y) + 1#w := by
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a general principle, unless there is a clear local convention, I would like all theorems with primes in their names to have a doc-comment explaining the naming convention / name clash / difficulty in naming.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hargoniX can comment here as to his convention for the bv_normalize code, as I think I'm following his local convention. (The lemma is specifically applied to the normalized form where negation has been expanded to twos complement, and isn't 'user-facing' in that sense.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lemmas should be named neg_mul (note that they exist in their own namespace separate from regular BitVec theory so you can reuse the name). The reason that there is ' lemmas above is that you also need to match on (1#w + ~~~x) * y = ~~~(x * y) + 1#w. This is what the symmetries remark in the spreadsheet is alluding to. If you read the Bitwuzla code you will see they have a function is_bv_neg https://github.com/bitwuzla/bitwuzla/blob/main/src/rewrite/rewriter.cpp#L293 that checks for all these symmetries.

example (x y : BitVec 16) : (~~~x + 1) * y = ~~~(x * y) + 1 := by bv_normalize
example (x y : BitVec 16) : x * (~~~y + 1) = ~~~(x * y) + 1 := by bv_normalize
example (x y : BitVec 16) : (~~~x + 1) * (~~~y + 1) = x * y := by bv_normalize

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a difference in the behaviour of bv_decide after adding these normalizations? If so, can we have a test demonstrating it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question, I don't think there is an easily testable difference from bv_decides perspective.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general the idea here is mostly to copy what Bitwuzla is doing, given that Bitwuzla has done extensive experimentation with rewrites on BitVec it is likely that these rules are good for us. The purpose is mostly to reduce the pressure on the SAT solver/bitblaster further below. So for example when we have (~~~x + 1) * y and x * (~~~y + 1) in our goal somewhere these were previously mostly distinct multiplication circuits that the SAT solver had to reason about but they are now shared.

In the past the approach to verify that these rules actually bring benefit in practice ™️ has been to run it against SMTLIB on the Stanford cluster and observe speedups, though that is a very expensive operation (takes about half a day) so we don't do that for every rule, just every now and then to see that the trend is going upwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants