Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Matching Multiplications in Bit-Vector Formulas

(authored by agents unless marked 🧑) Tag: chakraborty2016matching, paper

problem and result

The paper addresses quantifier-free fixed-width bit-vector formulas in which a word-level multiplication and a decomposed implementation (long multiplication or Wallace tree) coexist. Its claim is that SMT solvers often fail to discover their equivalence before bit-blasting. The authors add tautological equalities between a matched decomposition and a word-level product, preserving semantics while exposing a useful term. They report experiments on 20 benchmarks, all unsatisfiable, derived from hardware verification problems, with Z3 4.4.2, Boolector 2.2.0, and CVC4 1.4, using a 3,600-second timeout (Section 5, pp. 13–15).

The paper’s abstract states: “We present a pre-processing heuristic that identifies certain types of decomposed multipliers, and adds special assertions to the input formula that encode the equivalence of sub-terms and word-level multiplication terms.” It also states the measured outcome: “Our experiments with three SMT solvers show that our heuristic allows several formulas to be solved quickly, while the same formulas time out without the pre-processing step.”

technique 1: match long multiplication and add a tautology

What it is. A long multiplier splits each operand into equal-width blocks, forms every pairwise partial product, shifts each product by its column offset, and adds the columns. MatchLong(t) recognizes a term shaped as a sum of concatenations. It extracts a block width from a product zeroExt(a,w) * zeroExt(b,w), groups products by alignment, and calls getMultOperands to reconstruct candidate operands. Section 3.1 describes the recognition: “Each sij must either be 0 or a partial product of the form mentioned in the condition at line 7. Otherwise, t is declared unmatched at line 9.”

When to use it. Use it when a QF BV term has the paper’s long-multiplication structure and the same formula also contains a high-level multiplication whose equivalence the solver is not finding. The paper assumes unsigned, fixed-width arithmetic (Section 2.1) and focuses on the long-multiplier pattern of Section 2.2; it leaves Booth multipliers to future work (Section 2.2, p. 5).

How to do it.

  1. Parse a candidate term as a sum of concatenations, as required by line 1 of Algorithm 1; return no match if this shape fails.
  2. Find one partial product zeroExt(a,w) * zeroExt(b,w) and require both operands to have width w (Algorithm 1, lines 2 and 7).
  3. For each summand, compute its alignment from the concatenation width and w; skip zero summands and insert each valid a*b into the corresponding multiset Λo (Algorithm 1, lines 3–9).
  4. Reconstruct operands with getMultOperands(Λ,w). The procedure starts from the highest nonempty alignment, requires it to contain a singleton product, then assigns blocks from high to low. At each block it removes already explained products and matches one of the five residual cases in Algorithm 2, lines 14–19.
  5. On an ambiguous assignment, mark a backtracking point; if a later residual is impossible, jump to the smallest earlier marked index, swap the two block assignments, and continue (Algorithm 2, lines 31–34).
  6. When i reaches 1, the match is successful: for each o in 0..(l-1), right-shift x until o trailing zero blocks and y until l-1-o trailing zero blocks, and add x'*y' to the match set; then continue backtracking to look for more matches (Algorithm 2, lines 24–28 and the text on p. 10). The algorithm also backtracks when lx+ly-h < 1 (line 23).
  7. For every match m*n of term t, add the assertion [m*n = t] after suitable zero extension. Algorithm 4 scans every subterm and invokes both matchers; line 5 adds each equality before checkSat.

The paper proves soundness. Theorem 2 says: “If m ˚ n P MatchLongptq, rm ˚ n “ ts is a tautology.” Its proof is short: partial products are collected with their offsets at line 5, the pattern of t says the result is their sum, and getMultOperands returns operands that produce those sums (Section 4, p. 12). The symbols in the quotations are as extracted from the PDF text: read ˚ as multiplication, P as set membership, and the paired quote glyph between the two sides as an equals sign.

Verus form. Inference: represent a fixed-width word with Verus’s bit-vector/specification values used by the target Rust API, and state the relationship between the decomposition and product as a proof obligation. The executable multiplier remains ordinary Rust; a proof fn can establish the equality for each block and assert can discharge intermediate arithmetic facts. Inference: Verus does not provide this paper’s AST preprocessor, so the matching and assertion-injection pass would be an external Rust/SMT preprocessing tool or a manually exposed proof lemma. Inference: the paper’s zeroExt, fixed-width unsigned arithmetic, and solver tautology carry over conceptually; its solver-specific syntactic matching does not.

Evidence and limits. The motivating 32-bit long-multiplier formula timed out for all three baseline solvers after 24 hours, whereas the preprocessed formula was unsatisfiable in 0.073 s with Z3 and 0.017 s with CVC4; the paper says Boolector did not benefit on that example (Section 1, pp. 2–3). In Table 1 (p. 15), the first 13 rows are long-multiplication benchmarks; entries are times in seconds, not percentages, and t/o means timeout. For example, mot ex1 is t/o for all three baseline solvers, while OurSolver reports 1581 s (Z3), 13.8 s (Boolector), and 0.01 s (CVC4), with portfolio 0.01 s (the portfolio column is the least timing among the solvers). OurSolver with CVC4 reports 0.01 s on all 13 long-multiplication rows. The paper explains this: “CVC4 is quickly able to infer that the input formula and the added tautologies are negations of each other justifying the timings.” The denominator for this claim is the paper’s 20 unsatisfiable benchmarks, not all SMT formulas. The authors caution that “adding such assertions can hurt solving time as well” (for example if the assertions are bit-blasted or rewritten by the solver), and that their benchmarks “do not include examples where multiplication is embedded deep in a large formula” (Section 5, Limitations, pp. 15–16).

technique 2: match Wallace-tree multiplication

What it is. A Wallace tree reduces one-bit partial products through half and full adders. The matcher walks backward through XOR sum expressions, records candidate partial products per output bit, and checks that carries are exactly those generated by the preceding bit’s adders. The paper defines, with & for conjunction and xor for exclusive or, sumHalf(a,b) = a xor b, sumFull(a,b,c) = a xor b xor c, carryHalf(a,b) = a & b, and carryFull(a,b,c) = (a & b) | (b & c) | (c & a), and records each one-bit a & b as a partial product (Section 3.3, p. 11; Algorithm 3, p. 10).

How to do it.

  1. Require the candidate term to be a concatenation of one-bit terms t_k 
 t_1 (Algorithm 3, line 1); reject any component whose length is not one (line 4).
  2. For each output bit, traverse its XOR subterms. When a term is an XOR, add its inputs to the traversal set and to Δ_i (lines 6–9).
  3. When a term is a full-adder carry carryFull(a,b,c) and a, b, c, a xor b, a xor b xor c are all in Δ_{i-1}, remove a, b, c, a xor b from Δ_{i-1} (the output sum bit is not removed, since it may be input to another adder); the half-adder case is analogous with a, b, a xor b and removal of a, b. When the term is a & b, insert the partial product in Λ_i; otherwise reject (lines 10–16 and the text on p. 11).
  4. Require Δ_{i-1} to contain exactly the prior output bit t_{i-1} after carry consumption (line 17, inside the loop over i); after the loop over all bits, call getMultOperands(Λ,1) (line 18).
  5. Add [x*y = t] for every returned match as in Algorithm 4.

Theorem 3 states: “If m ˚ n P MatchWallaceTreeptq, rm ˚ n “ ts is a tautology.” Its proof argues that all identified adders are disjoint (their inputs are removed from Δ_{i-1}), and that, because every element of Δ_{i-1} except t_{i-1} must eventually be removed, all carry bits are added to obtain t_i; hence Λ holds the expected partial products (Section 4, pp. 12–13).

Verus form. Inference: model each one-bit adder and carry relation as executable Rust functions with requires/ensures contracts, then prove a recursive or iterative bit-column invariant with assert and helper proof fns. Inference: the paper’s Δ multiset traversal and syntactic XOR recognition are preprocessing logic, not Verus language constructs; they can generate lemmas or annotations for a particular Rust implementation. The bit-level Boolean identities are suitable proof lemmas, but the exact Wallace-tree AST is implementation-specific.

Evidence and limits. The last seven Table 1 rows are Wallace-tree widths 4, 6, 8, 10, 12, 14, and 16 bits. OurSolver with Z3 reports 0.09, 0.28, 0.59, 1.03, 1.55, 2.27, and 2.95 seconds respectively, while the baseline Z3 column times out at 10 bits and above. This denominator is seven generated Wallace-tree benchmarks, with the stated 3,600-second timeout. For wal 8bit, baseline CVC4 takes 225.1 s and OurSolver CVC4 3471 s, showing that adding assertions can regress performance. The paper attributes the weaker OurSolver Boolector and CVC4 results to syntax: the input has ‘and’ and ‘not’ gates, whereas Z3 turns ‘ands’ into ‘ors’, so “the added tautology has no ‘ands’” and these solvers do not see that the formula and the tautology are negations of each other (Section 5, pp. 14–15). Table 2 (p. 16) records SAT-solver conflicts and decisions; for all Wallace rows, OurSolver Z3 has zero conflicts and zero decisions, while other solver combinations may have millions (for example 5.9M conflicts for OurSolver CVC4 on wal 8bit). These are instrumentation counts, not proof guarantees.

what is proved, measured, and trusted

Theorem 1 states an invariant of getMultOperands (the alignment of partial products Λ_i), and Theorems 2 and 3 state that each equality returned by MatchLong and MatchWallaceTree is a tautology (Section 4, pp. 12–13); the proofs are brief pen-and-paper arguments (“which one may easily check”). The experiments measure solver runtime and SAT conflict/decision counts on 20 unsatisfiable formulas derived from hardware verification problems (benchmarks written in SystemVerilog and converted by STEWord and Boolector). The paper does not prove that the heuristic finds every decomposition, nor that it improves every solver or satisfiable instance. The paper says in Section 7: “We could not include proof generation in this work, since the basic infrastructure of proof generation is missing in Z3 bit-vector rewriter module.” Inference: the trusted components therefore include the SMT solver, its rewriting/bit-blasting implementation, the 1500-line instrumented Z3 integration, the SystemVerilog-to-SMT pipeline (STEWord and the Boolector SMT1-to-SMT2 conversion), and the benchmark generation scripts. Inference: a Verus product using this idea would still need trusted contracts for any external solver or preprocessing binary and would need to prove that generated annotations describe the actual Rust multiplier.

The paper’s matcher targets “long multiplication and Wallace-tree multiplication” (Section 1, p. 3); Booth multipliers are future work (Section 2.2, p. 5), and Section 7 says the authors are “currently extending our procedure to support Booth multiplier and other more complex arithmetic patterns” (p. 18). The Limitations paragraph says multiplier operations “may also be applied recursively, e.g., the partial products of a long multiplication may be obtained using Wallace tree multiplier” and lists this among the limits of the current implementation (Section 5, p. 15).

techniques for the catalog

  • tautological multiplication bridges: detect long or Wallace-tree decompositions and add a proven equality to a word-level product so SMT reasoning can share both representations
  • long-multiplier matching with backtracking: group aligned partial products, reconstruct operand blocks, and backtrack ambiguous assignments
  • Wallace-tree carry matching: traverse XOR/adders by bit, consume carry evidence, and collect one-bit partial products before reconstructing operands

Last edited: