Skip to content

Commit ffe136e

Browse files
committed
Symbolic Toom-Cook multiplication
Implements the algorithm of Section 4 of "Further Steps Down The Wrong Path : Improving the Bit-Blasting of Multiplication" (see https://ceur-ws.org/Vol-2908/short16.pdf).
1 parent 832578f commit ffe136e

File tree

2 files changed

+159
-2
lines changed

2 files changed

+159
-2
lines changed

src/solvers/flattening/bv_utils.cpp

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Author: Daniel Kroening, [email protected]
88

99
#include "bv_utils.h"
1010

11+
#include <util/arith_tools.h>
12+
1113
#include <utility>
1214

1315
bvt bv_utilst::build_constant(const mp_integer &n, std::size_t width)
@@ -782,7 +784,7 @@ bvt bv_utilst::dadda_tree(const std::vector<bvt> &pps)
782784
// trees (and also the default addition scheme), but isn't consistently more
783785
// performant with simple partial-product generation. Only when using
784786
// higher-radix multipliers the combination appears to perform better.
785-
#define DADDA_TREE
787+
// #define DADDA_TREE
786788

787789
// The following examples demonstrate the performance differences (with a
788790
// time-out of 7200 seconds):
@@ -924,7 +926,7 @@ bvt bv_utilst::dadda_tree(const std::vector<bvt> &pps)
924926
// with Dadda's reduction yields the most consistent performance improvement
925927
// while not regressing substantially in the matrix of different benchmarks and
926928
// CaDiCaL and MiniSat2 as solvers.
927-
#define RADIX_MULTIPLIER 8
929+
// #define RADIX_MULTIPLIER 8
928930

929931
#ifdef RADIX_MULTIPLIER
930932
static bvt unsigned_multiply_by_3(propt &prop, const bvt &op)
@@ -1861,6 +1863,155 @@ bvt bv_utilst::unsigned_karatsuba_multiplier(const bvt &_op0, const bvt &_op1)
18611863
return add(z0, z1);
18621864
}
18631865

1866+
bvt bv_utilst::unsigned_toom_cook_multiplier(const bvt &_op0, const bvt &_op1)
1867+
{
1868+
PRECONDITION(!_op0.empty());
1869+
PRECONDITION(!_op1.empty());
1870+
1871+
if(_op1.size() == 1)
1872+
return unsigned_multiplier(_op0, _op1);
1873+
1874+
// break up _op0, _op1 in groups of at most GROUP_SIZE bits
1875+
PRECONDITION(_op0.size() == _op1.size());
1876+
#define GROUP_SIZE 8
1877+
const std::size_t d_bits =
1878+
2 * GROUP_SIZE +
1879+
2 * address_bits((_op0.size() + GROUP_SIZE - 1) / GROUP_SIZE);
1880+
std::vector<bvt> a, b, c_ops, d;
1881+
for(std::size_t i = 0; i < _op0.size(); i += GROUP_SIZE)
1882+
{
1883+
std::size_t u = std::min(i + GROUP_SIZE, _op0.size());
1884+
a.emplace_back(_op0.begin() + i, _op0.begin() + u);
1885+
b.emplace_back(_op1.begin() + i, _op1.begin() + u);
1886+
1887+
c_ops.push_back(zeros(i));
1888+
d.push_back(prop.new_variables(d_bits));
1889+
c_ops.back().insert(c_ops.back().end(), d.back().begin(), d.back().end());
1890+
c_ops.back() = zero_extension(c_ops.back(), _op0.size());
1891+
}
1892+
for(std::size_t i = a.size(); i < 2 * a.size() - 1; ++i)
1893+
{
1894+
d.push_back(prop.new_variables(d_bits));
1895+
}
1896+
1897+
// r(0)
1898+
bvt r_0 = d[0];
1899+
prop.l_set_to_true(equal(
1900+
r_0,
1901+
unsigned_multiplier(
1902+
zero_extension(a[0], r_0.size()), zero_extension(b[0], r_0.size()))));
1903+
1904+
for(std::size_t j = 1; j < a.size(); ++j)
1905+
{
1906+
// r(2^(j-1))
1907+
bvt r_j = zero_extension(
1908+
d[0], std::min(_op0.size(), d[0].size() + (j - 1) * (d.size() - 1)));
1909+
for(std::size_t i = 1; i < d.size(); ++i)
1910+
{
1911+
r_j = add(
1912+
r_j,
1913+
shift(
1914+
zero_extension(d[i], r_j.size()), shiftt::SHIFT_LEFT, (j - 1) * i));
1915+
}
1916+
1917+
bvt a_even = zero_extension(a[0], r_j.size());
1918+
for(std::size_t i = 2; i < a.size(); i += 2)
1919+
{
1920+
a_even = add(
1921+
a_even,
1922+
shift(
1923+
zero_extension(a[i], a_even.size()),
1924+
shiftt::SHIFT_LEFT,
1925+
(j - 1) * i));
1926+
}
1927+
bvt a_odd = zero_extension(a[1], r_j.size());
1928+
for(std::size_t i = 3; i < a.size(); i += 2)
1929+
{
1930+
a_odd = add(
1931+
a_odd,
1932+
shift(
1933+
zero_extension(a[i], a_odd.size()),
1934+
shiftt::SHIFT_LEFT,
1935+
(j - 1) * (i - 1)));
1936+
}
1937+
bvt b_even = zero_extension(b[0], r_j.size());
1938+
for(std::size_t i = 2; i < b.size(); i += 2)
1939+
{
1940+
b_even = add(
1941+
b_even,
1942+
shift(
1943+
zero_extension(b[i], b_even.size()),
1944+
shiftt::SHIFT_LEFT,
1945+
(j - 1) * i));
1946+
}
1947+
bvt b_odd = zero_extension(b[1], r_j.size());
1948+
for(std::size_t i = 3; i < b.size(); i += 2)
1949+
{
1950+
b_odd = add(
1951+
b_odd,
1952+
shift(
1953+
zero_extension(b[i], b_odd.size()),
1954+
shiftt::SHIFT_LEFT,
1955+
(j - 1) * (i - 1)));
1956+
}
1957+
1958+
prop.l_set_to_true(equal(
1959+
r_j,
1960+
unsigned_multiplier(
1961+
add(a_even, shift(a_odd, shiftt::SHIFT_LEFT, j - 1)),
1962+
add(b_even, shift(b_odd, shiftt::SHIFT_LEFT, j - 1)))));
1963+
1964+
// r(-2^(j-1))
1965+
bvt r_minus_j = zero_extension(
1966+
d[0], std::min(_op0.size(), d[0].size() + (j - 1) * (d.size() - 1)));
1967+
for(std::size_t i = 1; i < d.size(); ++i)
1968+
{
1969+
if(i % 2 == 1)
1970+
{
1971+
r_minus_j = sub(
1972+
r_minus_j,
1973+
shift(
1974+
zero_extension(d[i], r_minus_j.size()),
1975+
shiftt::SHIFT_LEFT,
1976+
(j - 1) * i));
1977+
}
1978+
else
1979+
{
1980+
r_minus_j = add(
1981+
r_minus_j,
1982+
shift(
1983+
zero_extension(d[i], r_minus_j.size()),
1984+
shiftt::SHIFT_LEFT,
1985+
(j - 1) * i));
1986+
}
1987+
}
1988+
1989+
prop.l_set_to_true(equal(
1990+
r_minus_j,
1991+
unsigned_multiplier(
1992+
sub(a_even, shift(a_odd, shiftt::SHIFT_LEFT, j - 1)),
1993+
sub(b_even, shift(b_odd, shiftt::SHIFT_LEFT, j - 1)))));
1994+
}
1995+
1996+
if(c_ops.empty())
1997+
return zeros(_op0.size());
1998+
else
1999+
{
2000+
#ifdef WALLACE_TREE
2001+
return wallace_tree(c_ops);
2002+
#elif defined(DADDA_TREE)
2003+
return dadda_tree(c_ops);
2004+
#else
2005+
bvt product = c_ops.front();
2006+
2007+
for(auto it = std::next(c_ops.begin()); it != c_ops.end(); ++it)
2008+
product = add(product, *it);
2009+
2010+
return product;
2011+
#endif
2012+
}
2013+
}
2014+
18642015
bvt bv_utilst::unsigned_multiplier_no_overflow(
18652016
const bvt &op0,
18662017
const bvt &op1)
@@ -1913,6 +2064,8 @@ bvt bv_utilst::signed_multiplier(const bvt &op0, const bvt &op1)
19132064

19142065
#ifdef USE_KARATSUBA
19152066
bvt result = unsigned_karatsuba_multiplier(neg0, neg1);
2067+
#elif defined(USE_TOOM_COOK)
2068+
bvt result = unsigned_toom_cook_multiplier(neg0, neg1);
19162069
#else
19172070
bvt result=unsigned_multiplier(neg0, neg1);
19182071
#endif
@@ -1986,6 +2139,9 @@ bvt bv_utilst::multiplier(
19862139
#ifdef USE_KARATSUBA
19872140
case representationt::UNSIGNED:
19882141
return unsigned_karatsuba_multiplier(op0, op1);
2142+
#elif defined(USE_TOOM_COOK)
2143+
case representationt::UNSIGNED:
2144+
return unsigned_toom_cook_multiplier(op0, op1);
19892145
#else
19902146
case representationt::UNSIGNED: return unsigned_multiplier(op0, op1);
19912147
#endif

src/solvers/flattening/bv_utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class bv_utilst
8181

8282
bvt unsigned_multiplier(const bvt &op0, const bvt &op1);
8383
bvt unsigned_karatsuba_multiplier(const bvt &op0, const bvt &op1);
84+
bvt unsigned_toom_cook_multiplier(const bvt &op0, const bvt &op1);
8485
bvt signed_multiplier(const bvt &op0, const bvt &op1);
8586
bvt multiplier(const bvt &op0, const bvt &op1, representationt rep);
8687
bvt multiplier_no_overflow(

0 commit comments

Comments
 (0)