Skip to content

Commit 27e2fbd

Browse files
committed
chore: add implementation note for the NTT formula
1 parent f54fbf5 commit 27e2fbd

File tree

2 files changed

+232
-0
lines changed

2 files changed

+232
-0
lines changed
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# Incorrect range for Barrett reduction intermediate results
2+
3+
Original issue is here: https://github.com/zama-ai/tfhe-rs/issues/2037
4+
PR is here: https://github.com/zama-ai/tfhe-rs/pull/2748
5+
6+
## The problem
7+
8+
In order to compute the modular reduction of a value $v$ by a prime $p$, one wants to find $r$ such that:
9+
10+
$$
11+
v \equiv r \mod p
12+
$$
13+
14+
To avoid having to compute an actual modulo operation we rely on the euclidean division: for a given value $v$ and a divisor $p$ there exists a unique couple ($q$, $r$) with $r \lt p$ such that:
15+
16+
$$
17+
v = pq + r
18+
\iff r = v - pq
19+
$$
20+
21+
and
22+
23+
$$
24+
v \equiv r \mod p
25+
$$
26+
27+
Note that $q = \lfloor \frac{v}{p} \rfloor$.
28+
29+
The Barret reduction algorithm is explained and analyzed in this blog post: https://blog.zksecurity.xyz/posts/barrett-tighter-bound/ a major distinction to note is that the blog-post derives functions word-wise meaning that $b = 2^{32}$ or $2^{64}$. `tfhe-ntt` code is writtent in terms of bits so $b = 2$.
30+
31+
The `tfhe-ntt` code uses the Barrett reduction algorithm to compute a good first approximation $q_{barrett}$ of the quotient $q$ of the division of a given value $v$ by $p$. This in turns allows to compute a first approximation $r_{barrett}$ of $r$:
32+
33+
$$
34+
r_{barrett} := v - pq_{barrett}
35+
$$
36+
37+
Then one can subtract $p$ until $r_{barrett}$ satisfies $r_{barrett} \lt p$ to get the true value of $r$:
38+
39+
$$
40+
\begin{align*}
41+
\text{while }r_{barrett} \gt p: \\
42+
r_{barrett} &:= r_{barrett} - p \\
43+
\end{align*}
44+
$$
45+
46+
As indicated in the blog post and in the original algorithm the first "guess" of q is off by at most 2, meaning:
47+
48+
$$
49+
q_{barrett} \in \{q - 2, q - 1, q\}
50+
$$
51+
52+
There is a risk of overflowing the integer types used for the implementation if $q_{barrett}$ is not equal to q, which happens frequently. There are hard thresholds that guarantee no overflows for all primes.
53+
54+
For an unsigned integer of $w$ bits, we have in the worst case:
55+
56+
$$
57+
r_{barrett} = v - pq + 2p
58+
\iff r_{barret} = r + 2p
59+
$$
60+
61+
as $r \lt p$ then we have:
62+
63+
$$
64+
r_{barrett} \lt 3p
65+
$$
66+
67+
and the correctness condition to avoid overflow is:
68+
69+
$$
70+
r_{barrett} \lt 2^w
71+
\iff 3p \lt 2^w
72+
\iff p \lt \frac{2^w}{3}
73+
$$
74+
75+
The ZK Security blog post indicates that we can do better for certain primes beyond that threshold.
76+
Here we will be using the notations from the code which are shared with this paper: https://eprint.iacr.org/2021/420
77+
78+
The relevant rust code doing Barrett reduction for 32 bits is the following:
79+
80+
```Rust
81+
fn mul_accumulate_scalar(
82+
acc: &mut [u32],
83+
lhs: &[u32],
84+
rhs: &[u32],
85+
p: u32,
86+
p_barrett: u32,
87+
big_q: u32,
88+
) {
89+
let big_q_m1 = big_q - 1;
90+
91+
for (acc, lhs, rhs) in crate::izip!(acc, lhs, rhs) {
92+
let lhs = *lhs;
93+
let rhs = *rhs;
94+
95+
let d = lhs as u64 * rhs as u64;
96+
let c1 = (d >> big_q_m1) as u32;
97+
let c3 = ((c1 as u64 * p_barrett as u64) >> 32) as u32;
98+
let prod = (d as u32).wrapping_sub(p.wrapping_mul(c3));
99+
let prod = prod.min(prod.wrapping_sub(p));
100+
101+
let acc_ = prod + *acc;
102+
*acc = acc_.min(acc_.wrapping_sub(p));
103+
}
104+
}
105+
```
106+
107+
$p_{barrett}$ is the equivalent of the $\mu$ from the ZK Security blog, it's the precomputed constant for the algorithm.
108+
109+
For the notations here we have:
110+
111+
$$
112+
2^{Q-1} \le p \le 2^{Q}
113+
$$
114+
115+
So Q is about the number of bits required to represent $p$.
116+
117+
$p_{barrett}$ is computed as follows:
118+
119+
$$
120+
\begin{align*}
121+
L &= Q + 31 \\
122+
p_{barrett} &= \lfloor\frac{2^L}{p}\rfloor
123+
\end{align*}
124+
$$
125+
126+
Here we have a value $d$ we want to reduce modulo $p$. Following the computations of the `mul_accumulate_scalar` function from above we see the following for the $q_{barrett}$ value:
127+
128+
$$
129+
c1 = \lfloor\frac{d}{2^{Q-1}}\rfloor \\
130+
c3 = \lfloor\frac{c1 \cdot p_{barrett}}{2^{32}}\rfloor
131+
$$
132+
133+
Given the usage of $c3$ we can see it is actually $q_{barrett}$.
134+
135+
The ZK Security blog then re arranges the formula to derive interesting properties, you can first read their derivation with $b = 2$, as the following is based on their method only adapted to our particular case.
136+
137+
Now replacing the various terms by the way they were computed we get:
138+
139+
$$
140+
q_{barrett} = c3 = \lfloor\frac{\lfloor\frac{d}{2^{Q-1}}\rfloor\lfloor\frac{2^L}{p}\rfloor}{2^{32}}\rfloor
141+
$$
142+
143+
Let's define $\alpha \equiv d \mod {2^{Q-1}}$, let's write the euclidean division formula for d:
144+
145+
$$
146+
d = \lfloor\frac{d}{2^{Q-1}}\rfloor 2^{Q - 1} + \alpha
147+
\iff \lfloor\frac{d}{2^{Q-1}}\rfloor = \frac{d - \alpha}{2^{Q - 1}}
148+
$$
149+
150+
Defining in the same way $\beta \equiv 2^L \mod p$ we have:
151+
152+
$$
153+
\lfloor\frac{2^L}{p}\rfloor = \frac{2^L - \beta}{p}
154+
$$
155+
156+
157+
Recall $L = Q + 31$ and we have:
158+
159+
$$
160+
\begin{align*}
161+
q_{barrett}
162+
&= \lfloor \frac{\frac{d - \alpha}{2^{Q - 1}}\cdot\frac{2^{Q + 31} - \beta}{p}}{2^{32}}\rfloor \\
163+
&= \lfloor \frac{(d - \alpha)\cdot(2^{Q + 31} - \beta)}{p \cdot 2^{Q + 31}}\rfloor \\
164+
&= \lfloor \frac{d}{p} - {\color{red}{\frac{\alpha \cdot 2^{Q + 31} + \beta \cdot (d - \alpha)}{p \cdot 2^{Q + 31}}}} \rfloor
165+
\end{align*}
166+
$$
167+
168+
Let's call the red part $z$.
169+
170+
We have
171+
172+
$$
173+
q_{barrett } = \lfloor \frac{d}{m} - {\color{red}{z}} \rfloor
174+
$$
175+
176+
The floor function inequality gives us $\lfloor x \rfloor + \lfloor y \rfloor + 1 \ge \lfloor x + y \rfloor$
177+
178+
$$
179+
\lfloor \frac{d}{p} - z \rfloor + \lfloor z \rfloor + 1 \ge \lfloor \left(\frac{d}{p} - z\right) + z \rfloor = \lfloor \frac{d}{p} \rfloor = q
180+
$$
181+
182+
Therefore:
183+
184+
$$
185+
q_{barrett} + \lfloor z \rfloor + 1 \ge q
186+
$$
187+
188+
If $0 \le z \lt 2$ then $\lfloor z \rfloor \le 1$ which then means $q_{barrett} + 2 \ge q_{barrett} + \lfloor z \rfloor + 1 \ge q$.
189+
190+
The code selects $p \lt 2^{31}$ as valid primes, meaning $Q = 31$
191+
192+
Recall $\alpha \lt 2^{Q-1} = 2^{30}$ and observe that $d \lt 2^{62}$ because $d$ is a product of two values that are smaller than $p$.
193+
194+
Then:
195+
196+
$$
197+
\begin{align*}
198+
z &= \frac{\alpha \cdot 2^{62} + \beta \cdot (d-\alpha)}{m \cdot 2^{62}} \\
199+
&\lt \frac{{\color{red}{2^{Q - 1}}} \cdot 2^{62} + \beta \cdot {\color{red}{2^{62}}}}{p\cdot 2^{62}} \\
200+
&= \frac{2^{Q - 1} + \beta}{p}
201+
\end{align*}
202+
$$
203+
204+
We know always have $2^{Q - 1} \lt p$ and $\beta$ is a value $\mod p$ so we have:
205+
206+
$$
207+
z \lt \frac{p + p}{p} \lt 2
208+
$$
209+
210+
Now assuming $Q \lt 31$ we know $z \lt 2$, let's go through the last derivation to find what the condition is to have $z \lt 1$.
211+
212+
$$
213+
z \lt \frac{2^{Q - 1} + \beta}{p}
214+
$$
215+
216+
So $z \lt 1$ holds if:
217+
218+
$$
219+
\frac{2^{Q - 1} + \beta}{p} \le 1
220+
$$
221+
222+
And finally:
223+
224+
$$
225+
\beta \le p - 2^{Q-1}
226+
$$
227+
228+
Recall $\beta \equiv 2^L \mod p$ and you have the formula used in this patch.

tfhe-ntt/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@
6868
///
6969
/// Chinese remainder theorem solution:
7070
/// The art of computer programming (Donald E. Knuth), section 4.3.2
71+
///
72+
/// Implementation notes on the single Barrett reduction code can be found at
73+
/// <https://github.com/zama-ai/tfhe-rs/blob/main/implementation_notes/tfhe-ntt/gh_issue_2037_barrett_range.md>
74+
/// Or from the repo root at: implementation_notes/tfhe-ntt/gh_issue_2037_barrett_range.md
7175
#[allow(dead_code)]
7276
fn implementation_notes() {}
7377

0 commit comments

Comments
 (0)