跳到正文
Go back

阅读论文《Homomorphic Multiple Precision Multiplication for CKKS and Reduced Modulus Consumption》

编辑此页

推荐阅读:本文是原始的深度问答记录(Q&A)。如果您想阅读经过整理、结构更清晰的技术博客版本,请访问:深度解析:CKKS 同态加密中的多精度乘法优化 (HMPM)

这篇文章的后续思考:第二篇文章

前言

本文记录了与 Gemini 3 关于论文《Homomorphic Multiple Precision Multiplication for CKKS and Reduced Modulus Consumption》的深度对话。论文作者:Jung Hee Cheon, Wonhee Cho, Jaehyung Kim, 和 Damien Stehlé。

论文核心内容概述

核心痛点与目标

核心技术创新

扩展与应用

实验结果


第一部分:基础概念与动机

Q1: RNS-CKKS 与方案兼容性

问题:这个方案不是基于 RNS-CKKS 的吗?为什么说乘法比特数越大,模数消耗越多?Euclidean division 是什么?为什么密文的低位是在 R2R^2 这个数域中?

回答:

1.1 这个方案是基于 RNS-CKKS 的

是的,这个方案完全兼容并且是基于 RNS-CKKS 实现的。

虽然在论文的理论部分(Section 1 到 4),为了公式推导的简洁性,作者通常使用一个大的整数模数 QQ 来描述(比如写成 RQR_Q),这在密码学论文中很常见,为了避免符号过于复杂。

但在实验部分(Section 6) 和 具体实现讨论(Section 4.2 Modulus consumption) 中,作者明确指出:

所以,理论上讲它是通用的,但工程上它就是为了 RNS-CKKS 设计的优化。

1.2 为什么说”乘法比特数越大,模数消耗越多”?

这句话的逻辑链条如下:

  1. 精度与缩放因子(Scaling Factor Δ\Delta)的关系:在 CKKS 中,为了支持小数运算并保持精度,我们把消息 mm 放大 Δ\Delta 倍,变成整数形式存储。如果你想要处理更大比特数(即更高精度或更大数值范围)的乘数,你需要更大的缩放因子 Δ\Delta 来保证在噪声影响下依然能正确解码有效位。

  2. 乘法带来的膨胀:当你计算两个密文相乘 ct1×ct2ct_1 \times ct_2 时,底层的消息变成了 (m1⋅Δ)×(m2⋅Δ)=m1m2⋅Δ2(m_1 \cdot \Delta) \times (m_2 \cdot \Delta) = m_1 m_2 \cdot \Delta^2。此时缩放因子变成了 Δ2\Delta^2。

  3. Rescaling(重缩放)消耗模数:为了让后续计算能继续进行,必须把缩放因子从 Δ2\Delta^2 降回 Δ\Delta。这是通过 Rescaling 操作完成的,本质上是把密文除以 Δ\Delta。而在 CKKS 中,除以 Δ\Delta 是通过模数切换(Modulus Switching) 实现的:模数从 QQ 变成了 Q/ΔQ/\Delta。

结论:

1.3 Euclidean division 是什么?

Euclidean division 就是我们在小学数学里学的带余除法(欧几里得除法)。

对于任意整数 aa (被除数) 和 bb (除数,且 b≠0b \neq 0),存在唯一的整数 qq (商) 和 rr (余数),满足: a=b⋅q+ra = b \cdot q + r 其中 0≤r<∣b∣0 \le r < |b|。

在论文中的应用:

论文把这个概念应用到了密文(或者说是多项式的系数)上。作者想把一个大密文 ctct 分解成两部分。选定一个除数 2k2^k(或者文中的 qdivq_{\text{div}}),那么密文就可以表示为:

ct=2k⋅ct^+ctˇct = 2^k \cdot \hat{ct} + \check{ct}

这就像把数字 1234512345 按照 100100 分解:12345=100×123+4512345 = 100 \times 123 + 45。123123 是高位,4545 是低位。论文正是利用这种分解,把一个大精度的乘法拆成了几个小精度的乘法来做。

1.4 关于 1.1 节截图中的两个问题

问题 A:为什么密文的低位 ctˇ\check{ct} 是在 R2R^2 这个数域中(而不是 RQ2R_Q^2)?

这里 RR 指的是整数多项式环 Z[X]/(XN+1)\mathbb{Z}[X]/(X^N+1)。

问题 B:为什么解密后的误差项满足 eˇ=2ke^\check{e} = 2^k \hat{e}?

这是一个通过线性关系推导出来的结果,目的是为了保证总和不变。

  1. 分解关系: 密文被分解为 ct=2k⋅ct^+ctˇct = 2^k \cdot \hat{ct} + \check{ct}。
  2. 解密即线性运算: 解密函数 Dec(⋅)\text{Dec}(\cdot) 是线性的。所以: m=Dec(ct)=2k⋅Dec(ct^)+Dec(ctˇ)m = \text{Dec}(ct) = 2^k \cdot \text{Dec}(\hat{ct}) + \text{Dec}(\check{ct})
  3. 定义误差:
    • 设理想的商是 m^\hat{m},实际解密出的是 Dec(ct^)=m^+e^\text{Dec}(\hat{ct}) = \hat{m} + \hat{e}(e^\hat{e} 是商的误差)。
    • 设理想的余数是 mˇ\check{m},实际解密出的是 Dec(ctˇ)\text{Dec}(\check{ct})。
    • 我们知道原消息 m=2km^+mˇm = 2^k \hat{m} + \check{m}。
  4. 代入等式: 2km^+mˇ=2k(m^+e^)+Dec(ctˇ)2^k \hat{m} + \check{m} = 2^k (\hat{m} + \hat{e}) + \text{Dec}(\check{ct}) 2km^+mˇ=2km^+2ke^+Dec(ctˇ)2^k \hat{m} + \check{m} = 2^k \hat{m} + 2^k \hat{e} + \text{Dec}(\check{ct}) 消去 2km^2^k \hat{m},得: mˇ=2ke^+Dec(ctˇ)\check{m} = 2^k \hat{e} + \text{Dec}(\check{ct}) 移项得: Dec(ctˇ)=mˇ−2ke^\text{Dec}(\check{ct}) = \check{m} - 2^k \hat{e}
  5. 对应定义: 作者把 Dec(ctˇ)\text{Dec}(\check{ct}) 写成了 mˇ−eˇ\check{m} - \check{e} 的形式。 对比上面推导出的式子:mˇ−eˇ=mˇ−2ke^\check{m} - \check{e} = \check{m} - 2^k \hat{e}。 所以:eˇ=2ke^\check{e} = 2^k \hat{e}。

直观理解: 如果你在计算“商”(高位)的时候多算了 e^\hat{e},那么因为商是被乘了 2k2^k 的,所以你实际上多算了 2k⋅e^2^k \cdot \hat{e}。为了让总数保持不变(还是原来的 mm),你的“余数”(低位)就必须相应地少算 2k⋅e^2^k \cdot \hat{e} 来把这个误差抵消掉。

这就是文中提到的 “weak form of Euclidean division”(弱形式欧几里得除法):高位和低位各自解密出来可能不是完美的数学上的商和余数,因为它们互相包含了一个”互补”的误差项,但它们合起来依然能完美还原原始消息。


第二部分:Decompose 与模数消耗

Q2: Decompose 机制与实现细节

问题:

  1. 为什么论文中通过 decompose 能够减少模数消耗?
  2. “First, we need a way to homomorphically decompose a large bit-size plaintext into smaller pieces” 这个是什么意思?
  3. 1.1中的”Note that relying on smaller precision arithmetic for fixed-point or floating-point arithmetic is a standard technique”,这是什么意思,为什么要提这句话?
  4. 1.1中,Attempt 1 里明文属于 RQR^Q,为什么 Attempt 2 中密文却是 2k⋅Q2^k \cdot Q?对于 CKKS 方案,明文和密文多项式系数有什么大小限制吗?
  5. 1.1节最后的图中在说些什么?

回答:

2.1 为什么通过 Decompose(分解)能够减少模数消耗?

核心逻辑:将“大数的乘法”转化为“小数的乘法”,并舍弃微不足道的部分。

在标准 CKKS 中,模数消耗(Rescaling 消耗的比特数)直接取决于你的缩放因子(Scaling Factor Δ\Delta)。

Decompose 的做法: 它把一个大数 mm 拆成两半:高位 m1m_1 和 低位 m0m_0,即 m=m1⋅2k+m0m = m_1 \cdot 2^k + m_0。 这里 m1m_1 和 m0m_0 的数值范围都变小了(比如原来是 100-bit,现在两个都是 50-bit)。

2.2 “Homomorphically decompose a large bit-size plaintext into smaller pieces” 是什么意思?

这句话拆开来看:

  1. Large bit-size plaintext:一个数值很大的明文(例如 m=12345m=12345)。
  2. Smaller pieces:把它拆成两个较小的数,比如按 100100 拆分,拆成商 123123 和余数 4545。
  3. Homomorphically(同态地):这是最难的地方。
    • 如果你有私钥,解密出 12345,算出 123 和 45,再加密回去,这很简单。
    • 但是”同态”意味着你手里只有密文 Enc(12345)Enc(12345),你没有私钥,却要通过计算生成两个新的密文 Enc(123)Enc(123) 和 Enc(45)Enc(45)。

为什么这很难? 因为“取余数”和“整除”是非线性运算,在同态加密(尤其是 CKKS 这种近似计算)中非常难以实现。这篇论文的创新点就在于提出了一种高效的(虽然是近似的)方法来在密文状态下完成这个拆分。


2.3 关于 1.1 中提到的”Standard technique for fixed-point…”

意思是:不要觉得”扔掉低位乘低位”这个操作很奇怪,这在经典计算机科学里是很常见的操作。

2.4 Attempt 1 和 Attempt 2 的模数问题,以及系数限制

这里你观察得很细致!

Attempt 1 vs Attempt 2:

CKKS 的系数限制:

2.5 这个截图(1.1节最后)到底在说什么?

这段话是在解释论文提出的**“弱形式同态欧几里得除法”(Weak Homomorphic Euclidean Division)**的原理,以及为什么即便有误差,它也是可用的。

逻辑流:

  1. 目标: 我们把密文 ctct 拆成了 (ct^,ctˇ)( \hat{ct}, \check{ct} ),希望 ct^\hat{ct} 是商(高位),ctˇ\check{ct} 是余数(低位)。理想情况是:m=2k⋅商+余数m = 2^k \cdot \text{商} + \text{余数}。
  2. 现实问题: CKKS 解密是有噪声(Error)的。所以解密出来的商 Dec(ct^)\text{Dec}(\hat{ct}) 不一定等于完美的整数商 m^\hat{m}。
    • 设:Dec(ct^)=m^+e^\text{Dec}(\hat{ct}) = \hat{m} + \hat{e} (e^\hat{e} 是商的误差)。
  3. 互补效应(核心):
    • 由于总的消息 mm 必须守恒(m=Dec(ct)m = \text{Dec}(ct)),即: m≈2k⋅Dec(ct^)+Dec(ctˇ)m \approx 2^k \cdot \text{Dec}(\hat{ct}) + \text{Dec}(\check{ct})
    • 如果你解密出的商多了一个误差 e^\hat{e},那么这一项被乘了 2k2^k,变成了 2ke^2^k \hat{e}。
    • 为了让等式成立,解密出的余数 Dec(ctˇ)\text{Dec}(\check{ct}) 就必须自动包含一个相反的误差来抵消它。
    • 所以余数的解密值变成了:mˇ−eˇ\check{m} - \check{e},其中 eˇ≈2ke^\check{e} \approx 2^k \hat{e}。
  4. 结论(Weak form):
    • 虽然“余数部分”的误差 eˇ\check{e} 很大(它是商误差的 2k2^k 倍),看起来很吓人。
    • 但是,因为 ctct 拆分后的余数多项式 ctˇ\check{ct} 本身系数很小(这是由分解操作强制保证的),所以解密出来的结果在数学上依然成立。
    • 这就叫“弱”形式: 我们得到的不是完美的“商”和“余数”,而是一个“带了一点误差的商”和一个“带了巨大补偿误差的余数”。但妙就妙在,只要把它们重新组合起来,误差会互相抵消,还原出原始的 mm。

简单总结截图的意思: “我们把密文拆了。虽然拆出来的‘商’和‘余数’各自都有点误差(尤其是余数的误差看起来很大),但因为解密公式是线性的,这些误差在重组时会正好抵消。所以这个拆分是安全的。”


第三部分:误差分析与精度控制

Q3: Decompose 的精度影响与误差抵消机制

问题:

  1. Decompose后最终结果的精度有影响吗?
  2. 对于解密密文的高位数据 ct^\hat{ct},将其解密得到 m^+e^\hat{m}+\hat{e},问题是,这个误差 e^\hat{e} 是随机的,而且我只会得到一个解密结果,我并不能推测出高位解密后的误差 e^\hat{e} 是多少,同样,对于解密密文的低位数据也是如此,那么我怎么可能确保高位和低位的解密误差抵消,从而保证结果正确?

回答:

这两个问题直击了该方案最反直觉的核心。别担心,你的困惑非常合理——通常我们在密码学里谈论”误差”时,默认它是随机且不可控的。但在这里,情况完全不同。

3.1 Decompose 后最终结果的精度有影响吗?

答案:有影响,但影响很小,且是可控的。

精度损失主要来自两个方面:

  1. 舍弃了“低位 ×\times 低位”项(算法设计层面的损失):

    • 在 Mult2\text{Mult}^2 算法中,为了节省模数,作者故意扔掉了 ctˇ1×ctˇ2\check{ct}_1 \times \check{ct}_2 这一项。
    • 想象你在算 12.001×13.00112.001 \times 13.001。
      • (12+0.001)×(13+0.001)=156+0.012+0.013+0.000001(12 + 0.001) \times (13 + 0.001) = 156 + 0.012 + 0.013 + \mathbf{0.000001}。
    • 那个 0.000001\mathbf{0.000001} 就是“低位 ×\times 低位”。扔掉它对结果的影响非常微小(远小于 CKKS 本身自带的噪声)。这在数值计算中是标准的舍入操作。
  2. 噪声放大的风险(数学层面的损失):

    • 虽然我们下面会说误差能抵消,但因为“低位部分” ctˇ\check{ct} 实际上承载了放大了 2k2^k 倍的误差(即 eˇ\check{e}),如果不加控制,经过多次乘法后,这个 eˇ\check{e} 可能会增长得比正常 CKKS 快一点。
    • 解决办法: 论文提出定期进行 Recombine(重组) 操作。把 (ct^,ctˇ)( \hat{ct}, \check{ct} ) 合并回一个普通密文 ctct,让那些大的正负误差物理上抵消掉,然后再重新分解。这相当于“重置”了误差状态,保证了长期运行的精度。

结论: 只要参数选得好(比如适时重组),最终精度和标准 CKKS 几乎是一样的。

3.2 既然误差是随机的,我怎么能保证高位和低位的误差刚好抵消?

这是一个极其精彩的问题!你的直觉是:“高位解密出 A+error1A + error1,低位解密出 B+error2B + error2,这俩 error 都是随机的,凭什么加起来正好是 0?”

真相是:低位的误差 eˇ\check{e} 根本不是随机的,它是被”强行制造”出来抵消高位误差的。

请看下面的推导,我会用一个**“切蛋糕”的例子**来打破这个迷思。

1. 数学上的”强制绑定” 并不是先算出高位,再独立算出低位。低位是根据高位算出来的。

论文中的分解过程(DCP\text{DCP})本质上执行了以下逻辑:

  1. 计算商(高位): ct^=Round(ct/2k)\hat{ct} = \text{Round}(ct / 2^k)。
    • 这一步确实引入了随机误差,因为 Round\text{Round} 会导致精度丢失。
  2. 计算余数(低位): ctˇ=ct−2k⋅ct^\check{ct} = ct - 2^k \cdot \hat{ct}。
    • 注意! 这里是严格的等式。余数被定义为“原数减去(除数乘以商)”。

2. 解密时的”魔法”

现在我们来解密(记解密函数为 D(⋅)D(\cdot),它是一个线性函数,即 D(A+B)=D(A)+D(B)D(A+B) = D(A)+D(B))。

我们把上面的等式两边同时解密: D(ctˇ)=D(ct−2k⋅ct^)D(\check{ct}) = D(ct - 2^k \cdot \hat{ct}) D(ctˇ)=D(ct)−2k⋅D(ct^)D(\check{ct}) = D(ct) - 2^k \cdot D(\hat{ct})

移项得到: D(ct)=2k⋅D(ct^)+D(ctˇ)D(ct) = 2^k \cdot D(\hat{ct}) + D(\check{ct})

这说明什么?

3. 直观类比:切绳子

假设你有一根绳子,总长度是 100.5 米(这就好比原密文 ctct)。

  1. 你想把它切成两段,一段是整数部分(高位),一段是小数部分(低位)。
  2. 你测量了一下,觉得整数部分应该是 50 米,于是你切了 50 米下来(这是 ct^\hat{ct})。
  3. 那么剩下那段是多少?必然是 50.5 米(这是 ctˇ\check{ct})。
    • 当你把它们拼回去:50+50.5=100.550 + 50.5 = 100.5。结果正确。

现在,假设你“高位”切错了(产生了随机误差):

  1. 你手抖了,第一刀切成了 51 米(高位误差 e^=+1\hat{e} = +1)。
  2. 那么剩下那段绳子是多少?物理上必然变成了 49.5 米(低位自动变成了 eˇ=−1\check{e} = -1)。
  3. 当你解密(把两段绳子拼起来)时: 51 (高位)+49.5 (低位)=100.551 \text{ (高位)} + 49.5 \text{ (低位)} = 100.5 结果依然正确!

总结

你不需要推测误差是多少,也不需要手动去抵消它。

误差抵消是由代数结构保证的。因为 ctˇ\check{ct} 的定义就是 ct−2kct^ct - 2^k \hat{ct},所以当你把它们按 2kct^+ctˇ2^k \hat{ct} + \check{ct} 加回去时,你只是把减去的东西又加了回来,结果必然还原回原始的 ctct(以及原始的 mm)。

所以,高位的误差是随机的,但低位的误差是”高位误差的镜像”,它们是互补的。


第四部分:Mult² 算法详解

Q4: Relinearization 和 Rescaling 的改进

问题:帮我详细解释 1.2 Multiplication of decomposed ciphertexts 中的 Relinearization and rescaling 部分,解释为什么要这么做。

回答:

这一部分确实比较绕,但如果抓住了**“高位误差放大”和”强制互补”**这两个核心逻辑,就很容易理解了。

为了实现分解密文的乘法,论文重新定义了 Relin\text{Relin}(重线性化)和 RS\text{RS}(重缩放)。我们来看看为什么要改,以及怎么改的。

4.1 为什么不能直接用标准的 Relin 和 RS?(痛点:误差放大)

假设我们有两个分解好的部分 (ct^,ctˇ)(\hat{ct}, \check{ct}),其中 ct≈2k⋅ct^+ctˇct \approx 2^k \cdot \hat{ct} + \check{ct}。

如果我们“天真”地对它们分别做标准操作:

结论: 我们绝对不能允许在高位 ct^\hat{ct} 上直接引入“未被低位补偿”的独立误差。

4.2 Relin²:如何安全地重线性化?

为了解决上述问题,作者设计了 Relin2\text{Relin}^2。它的核心思想是:先把高位”还原”到原来的量级,处理完再拆开。

步骤解析:

公式:Relin2(ct^,ctˇ):=DCP2k(Relin(2k⋅ct^))+(0,Relin(ctˇ))\text{Relin}^2(\hat{ct}, \check{ct}) := \text{DCP}_{2^k}(\text{Relin}(2^k \cdot \hat{ct})) + (0, \text{Relin}(\check{ct}))

这里做了三件事:

  1. 放大高位(2k⋅ct^2^k \cdot \hat{ct}):作者没有直接处理 ct^\hat{ct},而是先把它乘上 2k2^k。这样它的量级就和原始密文一样了。

  2. 在大量级上做 Relin:计算 Relin(2k⋅ct^)\text{Relin}(2^k \cdot \hat{ct})。此时引入的噪声 ee 是加在 2k⋅ct^2^k \cdot \hat{ct} 上的,也就是加在”总数”上的。这个噪声没有被额外放大。

  3. 重新分解(DCP):把处理完的结果再次通过 DCP\text{DCP} 拆成新的商和余数。妙处:这一步拆分会自动把刚才 Relin 引入的噪声分配到新的”低位”里去(就像我们之前讨论的”切绳子”原理)。

总结:Relin2\text{Relin}^2 避免了直接在高位加噪声,而是把噪声加在整体上,再通过分解操作把噪声”挤”到低位去。

4.3 RS²:如何安全地重缩放?

RS\text{RS}(Rescaling)的作用是把密文除以缩放因子 Δ\Delta 并取整。这也是一个会引入舍入误差的操作。

公式解析: RS2(ct^,ctˇ):=(RS(ct^)⏟新高位,RS(2k⋅ct^+ctˇ)−2k⋅RS(ct^)⏟新低位)\text{RS}^2(\hat{ct}, \check{ct}) := (\underbrace{\text{RS}(\hat{ct})}_{\text{新高位}}, \underbrace{\text{RS}(2^k \cdot \hat{ct} + \check{ct}) - 2^k \cdot \text{RS}(\hat{ct})}_{\text{新低位}})

这简直就是我们上一轮讨论的**“切绳子/互补误差”**逻辑的直接应用!

让我们看看为什么要这么写(假设 RS(x)≈x/Δ\text{RS}(x) \approx x / \Delta):

  1. 新高位(New High): 直接对高位做重缩放:NewHigh=RS(ct^)\text{NewHigh} = \text{RS}(\hat{ct})。

    • 这一步肯定有舍入误差:NewHigh=ct^Δ+ϵround\text{NewHigh} = \frac{\hat{ct}}{\Delta} + \epsilon_{round}。
    • 就像之前说的,这个 ϵround\epsilon_{round} 如果不处理,会被放大 2k2^k 倍。
  2. 新低位(New Low): 作者是怎么算低位的? NewLow=RS(Total)−2k⋅NewHigh\text{NewLow} = \text{RS}(\text{Total}) - 2^k \cdot \text{NewHigh} (注:公式里的 2k⋅ct^+ctˇ2^k \cdot \hat{ct} + \check{ct} 就是重组后的 Total 密文)。

    • 这步操作的物理含义: NewLow=近似总数−2k⋅近似高位\text{NewLow} = \text{近似总数} - 2^k \cdot \text{近似高位}
    • 因为 总数≈2k⋅高位+低位\text{总数} \approx 2^k \cdot \text{高位} + \text{低位}。
    • 如果 NewHigh\text{NewHigh} 包含了正向误差(切多了),那么在计算 NewLow\text{NewLow} 时,因为减去了 2k⋅NewHigh2^k \cdot \text{NewHigh},这个正向误差就被减掉了。
    • 结果: NewLow\text{NewLow} 会自动变成一个包含“负向补偿误差”的值。

总结: RS2\text{RS}^2 的做法是:先算高位,然后用“总数”减去“高位占的部分”来强行定义低位。 这样就强迫低位吸收了高位 Rescaling 过程中产生的所有舍入误差,保证了 2k⋅新高位+新低位≈RS(旧总数)2^k \cdot \text{新高位} + \text{新低位} \approx \text{RS}(\text{旧总数}) 的恒等关系。


全文总结

为什么要这么麻烦地设计 Relin2\text{Relin}^2 和 RS2\text{RS}^2?

一句话解释:为了防止“高位”上的微小操作误差被系数 2k2^k 放大成灾难。

所有这些操作的核心策略都是:

  1. 不要独立修改高位。
  2. 要么先还原成整体再修改(Relin²)。
  3. 要么利用代数恒等式 Total - High = Low,强行让低位去背锅(RS²)。

Q5: Relin² 的输入和数据结构

问题:Relin² 的输入是什么?是上一步 tensor 后的结果?以及 (0,Relin(ctˇ))(0, \text{Relin}(\check{ct})) 这里的 0 是什么意思?

回答:

这两个问题问到了算法的数据结构和运算逻辑的细节,非常棒。

5.1 Relin² 的输入是什么?

简短回答: 是的,它的输入正是 Tensor²(上一步) 的输出结果。

详细解释:

我们需要回顾一下 CKKS 乘法的标准流程:Tensor →\rightarrow Relin →\rightarrow Rescale。

  1. Tensor² 的输出(即 Relin² 的输入): 在标准 CKKS 中,两个密文相乘(Tensor)后,密文会膨胀。原本密文是 2 个多项式 (c0,c1)(c_0, c_1),相乘后变成了 3 个多项式 (d0,d1,d2)(d_0, d_1, d_2)。这被称为“度为 2 的密文”(Degree-2 ciphertext),因为它包含密钥 ss 的二次项 s2s^2。

    在 Mult2\text{Mult}^2 方案中,因为我们维护的是一对密文 (ct^,ctˇ)(\hat{ct}, \check{ct}),所以 Tensor² 的输出是一对“膨胀”了的密文:

    • ct^\hat{ct}:是一个包含 3 个多项式的密文(高位部分)。
    • ctˇ\check{ct}:也是一个包含 3 个多项式的密文(低位部分)。

    Relin² 的任务就是把这两个“膨胀”的 3 元素密文,压缩回正常的 2 元素密文 (c0′,c1′)(c'_0, c'_1)。

5.2 (0, Relin(č̌t)) 这里的 0 是什么意思?

简短回答:这里的 0 代表一个**“全零密文”(Zero Ciphertext),它是为了保持”密文对”格式**而存在的占位符。

详细逻辑:

公式如下: Relin2(ct^,ctˇ):=DCP2k(Relin(2k⋅ct^))⏟部分 A+(0,Relin(ctˇ))⏟部分 B\text{Relin}^2(\hat{ct}, \check{ct}) := \underbrace{\text{DCP}_{2^k}(\text{Relin}(2^k \cdot \hat{ct}))}_{\text{部分 A}} + \underbrace{(0, \text{Relin}(\check{ct}))}_{\text{部分 B}}

请注意,整个系统的基本数据单元是 “密文对” (Pair),即所有运算结果都必须长这样:(高位部分, 低位部分)。

我们来看看加号两边分别是什么:

最终的加法过程(向量加法):

Result=(Ahigh,Alow)+(0,Blow)=(Ahigh+0, Alow+Blow)=(Ahigh, Alow+Relin(ctˇ))\begin{aligned} \text{Result} &= (A_{high}, A_{low}) + (0, B_{low}) \\ &= (A_{high} + 0, \ A_{low} + B_{low}) \\ &= (A_{high}, \ A_{low} + \text{Relin}(\check{ct})) \end{aligned}

总结:

那个 0 就像我们在做向量运算 (x,y)+(0,z)(x, y) + (0, z) 时的那个 0 一样,只是为了让维度对齐,确保 Relin(ctˇ)\text{Relin}(\check{ct}) 只被加到结果的低位分量上,而不污染高位分量。


Q6: Growth of the low part(低位膨胀问题)

问题:详细介绍 1.2 中的 Growth of the low part。

回答:

这一部分描述了 Mult2\text{Mult}^2 算法面临的一个核心挑战,以及作者提出的解决方案。我们可以把它称为**“低位部分的膨胀问题”**。

6.1 现象:低位部分(Low Part)为什么会变大?

起初,当我们刚对一个密文进行分解(Decompose)时: ct=2k⋅ct^+ctˇct = 2^k \cdot \hat{ct} + \check{ct} 根据带余除法的定义,低位 ctˇ\check{ct} 是余数,所以它的系数绝对值很小,被严格限制在 ≤2k/2\le 2^k/2。

但是,当我们开始对分解后的密文进行乘法运算(特别是 Tensor2\text{Tensor}^2 步骤)时,低位部分会迅速变大。

原因分析: 回顾一下乘法的交叉项逻辑。假设我们有两个数 AA 和 BB: A≈2kA^+AˇA \approx 2^k \hat{A} + \check{A} B≈2kB^+BˇB \approx 2^k \hat{B} + \check{B}

Mult2\text{Mult}^2 试图计算 A×BA \times B。根据 Tensor2\text{Tensor}^2 的定义,新的低位部分(在逻辑上)大致对应于交叉项: NewLow≈A^⋅Bˇ+B^⋅Aˇ\text{NewLow} \approx \hat{A} \cdot \check{B} + \hat{B} \cdot \check{A} (注:这里为了直观忽略了具体的缩放因子和 2k2^k 的系数调整,只看量级)

结论: 经过一次乘法后,新的低位部分不再是简单的“余数”了,它混合了高位的信息,导致其数值量级显著增长。如果连续做多次乘法,这个膨胀会呈指数级累积。

6.2 危害:变大有什么问题?

如果低位部分 ctˇ\check{ct} 长得太大,会引发两个严重后果:

  1. 精度雪崩(主要原因):还记得 Mult2\text{Mult}^2 为了省模数,在 Tensor 步骤中直接丢弃了 ctˇ1⊗ctˇ2\check{ct}_1 \otimes \check{ct}_2 这一项吗?

    • 起初:因为 ctˇ\check{ct} 很小,所以”小 ×\times 小”微不足道,丢了也就丢了,误差很小。
    • 膨胀后:如果 ctˇ\check{ct} 变得很大,那么 ctˇ1⊗ctˇ2\check{ct}_1 \otimes \check{ct}_2 就变成了一个巨大的误差项。这时候再把它扔掉,就会导致计算结果严重偏离正确值。
  2. 可能溢出:如果 ctˇ\check{ct} 的系数增长到超过了模数(虽然我们在 R2R^2 中讨论,但实际实现还是有物理限制的),或者超过了为了保证”弱欧几里得除法”成立的界限,整个数学假设就会崩塌。

6.3 解决方案:重组与再分解 (Recombine and Decompose)

为了解决这个问题,作者提出了一种类似“定期重置”的策略。

策略名称: Recombine and Decompose (RCB ∘\circ DCP)

操作步骤: 当发现低位膨胀到一定程度(或者在进行了固定次数的乘法后),执行以下操作:

  1. Recombine (RCB): 把高位和低位拼回去,变成一个普通的单密文。 cttemp=2k⋅ct^+ctˇct_{temp} = 2^k \cdot \hat{ct} + \check{ct} 在这个加法过程中,低位中积累的那些“大数值”会被加到高位上去(进位),或者与高位的数值合并。

  2. Decompose (DCP): 立即对这个 cttempct_{temp} 再次执行分解操作。 ctnew_pair=DCP(cttemp)ct_{new\_pair} = \text{DCP}(ct_{temp})

效果:


4. 实验结论(来自 Figure 1)

论文在 1.2 节末尾和实验部分提到了一个具体的数据(参考 Figure 1):

总结


第五部分:参数优化与模数分配

Q7: 不对称分解的效率分析

问题:之前有提到,将密文维度从 100bits,分解为高位低位各 50bits,可以节省一半模数,那如果分解的大小不一致呢?比如高位 60bits,低位 40bits 这种情况下是怎样的?

回答:

这是一个非常敏锐的问题!这涉及到优化理论中的**“均值不等式”**原理在密码学参数选择中的应用。

简短的回答是:不对称分解(如 60/40)会导致效率降低。50/50 分解是数学上的最优解(即模数消耗最小)。

为了解释为什么,我们需要回到该方案中控制精度和模数消耗的核心公式。

7.1 核心公式回顾

在 Mult2\text{Mult}^2 方案中,我们要维持的总精度(Scaling Factor Δ\Delta)大约等于“分解因子”乘以“消耗掉的模数”: Δ≈qdiv×qℓ\Delta \approx q_{\text{div}} \times q_{\ell}

  1. qdivq_{\text{div}} (=2k= 2^k):决定了**低位部分(Low Part)**的大小。
    • 如果你选 k=40k=40,低位就是 40 bits。
    • 如果你选 k=60k=60,低位就是 60 bits。
  2. qℓq_{\ell}:是我们在做 Rescaling 时扔掉的模数(也就是我们要消耗的成本)。

制约条件(瓶颈): 论文在误差分析(Theorem 4.8 和 4.9)中指出,为了保证计算结果正确,扔掉的模数 qℓq_{\ell} 必须足够大,至少要比低位部分的平方稍大一点,否则低位的误差项 Low2qℓ\frac{\text{Low}^2}{q_{\ell}} 会爆炸。 简单来说,必须满足: qℓ≳qdiv(或者说 log⁡qℓ≥k)q_{\ell} \gtrsim q_{\text{div}} \quad (\text{或者说 } \log q_{\ell} \ge k) (注:严格来说误差项是 Low ×\times Low / qℓq_{\ell},如果要让这一项变小, qℓq_{\ell} 的比特数最好不小于 Low 的比特数)

7.2 场景分析:目标总精度 100 bits

我们现在的目标是 log⁡Δ=100\log \Delta = 100。这意味着 log⁡qdiv+log⁡qℓ≈100\log q_{\text{div}} + \log q_{\ell} \approx 100。

我们来看看不同切分方式的代价:

1. 对称分解(50 / 50)—— 最优解

2. 不对称:低位小,高位大(40 / 60)

即 k=40k=40(低位 40 bits),剩下的高位承载 60 bits 的数值。

3. 不对称:低位大,高位小(60 / 40)

即 k=60k=60(低位 60 bits),剩下的高位承载 40 bits 的数值。


总结

这就好比你要围一个 面积为 100 的矩形(面积代表总精度 Δ\Delta),而模数消耗是矩形的较长边(因为 qℓq_{\ell} 必须既补足精度,又要盖过 qdivq_{\text{div}},取两者的最大值)。

所以,均分(Symmetric Decomposition) 是让模数消耗降到最低(1/2log⁡Δ1/2 \log \Delta)的唯一数学极值点。任何偏离均分的操作,都会导致你需要消耗更多的模数来达到同样的精度,或者在同等模数下得到更差的精度。


Q8: 模数消耗的平方关系疑问

问题:你不是说”扔掉的模数 qℓq_\ell 必须足够大,至少要比低位部分的平方稍大一点”,为什么示例比较的时候却没有平方?

回答:

这是一个非常非常棒的质疑!你发现了我在上一条回答中为了简化直觉而表述不够严谨的地方。

让我根据论文的数学推导,为你精确地纠正这个概念。

结论先行:

你是对的,直觉上”低位平方”应该是 22k2^{2k}。但是,在 Mult2\text{Mult}^2 的算法结构中,有一个关键步骤(Tensor 后的隐式除法)抵消掉了一个 2k2^k。所以,最终的数学约束变成了**“线性关系”**,而不是”平方关系”。即:模数 qℓq_{\ell} 的比特数 ≥\ge 低位部分 qdivq_{\text{div}} 的比特数。

8.1 为什么”平方”变成了”线性”?(数学推导)

让我们打开论文 第 8 页 “Modulus consumption” 这一小节,作者在这里给出了精确的推导。

步骤 A:噪声源头确实是“平方”

在做 Tensor2\text{Tensor}^2 时,我们扔掉的项是 ctˇ1⊗ctˇ2\check{ct}_1 \otimes \check{ct}_2。

步骤 B:算法自带的“除法效应”

请注意 Mult2\text{Mult}^2 的逻辑是模拟 (A×B)/2k(A \times B) / 2^k 的效果(因为我们提取的是商)。 根据 Lemma 4.2,Tensor2\text{Tensor}^2 的结果实际上被除以了 qdivq_{\text{div}}(即 2k2^k)。

步骤 C:Rescaling 的除法

最后,我们执行 RS2\text{RS}^2(重缩放),这会把结果再除以模数 qℓq_{\ell}。

步骤 D:约束条件

为了让这个最终误差很小(比如小于 1,或者小于某个常数),分母必须大于分子: qℓ≥2kq_{\ell} \ge 2^k 两边取对数(Bit-size): log⁡qℓ≥k\log q_{\ell} \ge k

这就是为什么我在比较时是直接比较比特数(50 vs 50),而不是比较平方。


2. 重新审视 60/40 的例子

现在用这个精确的数学关系,我们再看一遍为什么“均分”最优。

假设目标总缩放因子 Δ=qdiv⋅qℓ\Delta = q_{\text{div}} \cdot q_{\ell},我们需要 log⁡Δ=100\log \Delta = 100。

方案 1:均分(50 / 50)

方案 2:低位大(60 / 40)—— 你的疑问点

方案 3:低位小(40 / 60)

总结

我在之前的回答中说“至少要比低位部分的平方稍大一点”,这是口语表达上的失误,容易产生歧义。更准确的说法是:

“模数 qℓq_{\ell} 必须足够大,以抵消掉‘低位平方项’在经过算法内部除法后残留的影响。”

而这个“残留的影响”,在比特数上,正好就等于低位的比特数。

所以:


Q9: Tensor² 中的除法效应

问题:你提到的”请注意 Mult² 的逻辑是模拟 (A×B)/2k(A×B)/2^k 的效果(因为我们提取的是商)。根据 Lemma 4.2,Tensor² 的结果实际上被除以了 qdivq_{\text{div}}(即 2k2^k)。“这不会对结果产生什么影响吗?

回答:

这是一个非常敏锐的直觉!通常我们在数学上把结果除以 2k2^k,数值不就变了吗?

但在这里,答案是:这不仅不会对结果产生负面影响,反而是为了维持计算正确性所”必须”的步骤。

原因在于 CKKS 的缩放因子(Scaling Factor)机制。

我用最通俗的逻辑给你拆解一下:

9.1 CKKS 的标准流程:必须”除掉”一部分

在 CKKS 中,我们不是直接计算 1.2×3.41.2 \times 3.4,而是计算放大后的整数。 假设缩放因子 Δ\Delta(比如 2402^{40})。

做乘法时发生了什么? (A⋅Δ)×(B⋅Δ)=(A⋅B)⋅Δ2(A \cdot \Delta) \times (B \cdot \Delta) = (A \cdot B) \cdot \Delta^2

注意到了吗?结果的缩放因子变成了 Δ2\Delta^2(即 2802^{80})。 如果不处理,下次再乘就会变成 Δ4\Delta^4,数值瞬间溢出。 所以,CKKS 算法强制要求:做完乘法后,必须把结果除以 Δ\Delta,让它变回 Δ\Delta 的规模。


2. Mult2\text{Mult}^2 的流程:分两步“除”

在 Mult2\text{Mult}^2 方案中,作者把这个“必须除掉的 Δ\Delta”拆成了两部分来实现。 设定:Δ≈qdiv×qℓ\Delta \approx q_{\text{div}} \times q_{\ell} (例如:Δ=2100\Delta = 2^{100},拆成 qdiv=250q_{\text{div}}=2^{50} 和 qℓ=250q_{\ell}=2^{50})。

第一步:Tensor2\text{Tensor}^2(隐式除法) 正如你引用的 Lemma 4.2,这一步让结果被除以了 qdivq_{\text{div}}。 此时结果的缩放规模从 Δ2\Delta^2 变成了: Δ2qdiv\frac{\Delta^2}{q_{\text{div}}}

第二步:RS2\text{RS}^2(显式除法) 这是算法最后的重缩放步骤,会再除以模数 qℓq_{\ell}。 此时结果的缩放规模变成了: Δ2qdiv⋅qℓ\frac{\Delta^2}{q_{\text{div}} \cdot q_{\ell}}

最终效果: 因为我们预先设计好了 qdiv⋅qℓ≈Δq_{\text{div}} \cdot q_{\ell} \approx \Delta,所以: 最终规模≈Δ2Δ=Δ\text{最终规模} \approx \frac{\Delta^2}{\Delta} = \Delta


3. 为什么 Lemma 4.2 会造成除法效果?

为了让你更放心,我们简单看一下 Lemma 4.2 为什么会产生“除以 qdivq_{\text{div}}”的效果。

逻辑如下:

  1. 我们的分解是 ct≈qdiv⋅ct^ct \approx q_{\text{div}} \cdot \hat{ct}(忽略低位)。
  2. 做乘法时,我们计算的是 Tensor2≈ct^1×ct^2\text{Tensor}^2 \approx \hat{ct}_1 \times \hat{ct}_2。
    • 这里 ct^1\hat{ct}_1 其实是 ct1qdiv\frac{ct_1}{q_{\text{div}}}。
    • 这里 ct^2\hat{ct}_2 其实是 ct2qdiv\frac{ct_2}{q_{\text{div}}}。
  3. 所以乘出来的结果 ≈ct1qdiv×ct2qdiv=ct1⋅ct2qdiv2\approx \frac{ct_1}{q_{\text{div}}} \times \frac{ct_2}{q_{\text{div}}} = \frac{ct_1 \cdot ct_2}{q_{\text{div}}^2}。
  4. 但是!Mult2\text{Mult}^2 的输出格式是“商和余数”。根据重建公式 Result=qdiv⋅High+LowResult = q_{\text{div}} \cdot \text{High} + \text{Low},这个结果会被乘回一个 qdivq_{\text{div}}。
  5. 综合算一下: (ct1⋅ct2qdiv2)×qdiv=ct1⋅ct2qdiv\left( \frac{ct_1 \cdot ct_2}{q_{\text{div}}^2} \right) \times q_{\text{div}} = \frac{ct_1 \cdot ct_2}{q_{\text{div}}}

总结

你担心的“结果被除了 2k2^k”是真实发生的,但这不是误差,而是功能。

这两者殊途同归,最终都把乘法产生的 Δ2\Delta^2 缩放回了 Δ\Delta,保证了后续计算可以继续进行。


Q10: RS² 中模数消耗减少的本质

问题:原来如此,是因为 RS² 中需要显示除法步骤中除以的模数变小了,因此才导致模数消耗更少?

回答:

完全正确!你已经抓住了这篇论文最本质的”省钱之道”。

简单来说:我们把原本需要”付费”完成的除法,拆成了一半”付费”和一半”免费”来做。

我来给你算一笔账,你立刻就明白了:

10.1 任务目标:把数字缩小 Δ\Delta 倍

假设为了维持精度,我们需要总的缩放因子 Δ=2100\Delta = 2^{100}。 也就是说,做完乘法后,我们需要把数据缩小 21002^{100} 倍。

2. 标准 CKKS 的做法(全额付费)

在标准 CKKS 中,缩小数据唯一的方法就是 Rescaling(重缩放)。

3. Mult2\text{Mult}^2 的做法(AA制,但有一半人逃单了)

作者把 Δ=2100\Delta = 2^{100} 拆成了两部分:qdiv=250q_{\text{div}} = 2^{50} 和 qℓ=250q_{\ell} = 2^{50}。

4. 最终对比

方案总缩放效果实际消耗的模数结论
标准 CKKS÷2100\div 2^{100}100 bits贵,能做的层数少
Mult2^2÷2100\div 2^{100}50 bits省了一半! 能做更多层

总结:

正是因为 RS2\text{RS}^2 这一步只需要处理剩余的那一半缩放(qℓq_{\ell}),所以它消耗的模数比标准做法小得多。而另一半缩放(qdivq_{\text{div}})被算法的结构设计给”白嫖”了。这就是这篇论文降低模数消耗的秘密武器。


Q11: 为什么不能白嫖 100%?

问题:那为啥不直接白嫖 100%?

回答:

这是一个非常符合经济学原理的提问:既然有免费午餐,为什么不全吃免费的?

答案非常残酷:因为”免费”的那部分(qdivq_{\text{div}}),副作用太大,必须用”付费”的那部分(qℓq_{\ell})来治病。

如果想白嫖 100%,你会直接死在噪声爆炸上。

我来给你演示一下,如果你尝试”白嫖 100%“会发生什么灾难。

11.1 贪心实验:试图白嫖 100%

假设目标总缩放因子 Δ=2100\Delta = 2^{100}。 你想让“免费的” qdiv=2100q_{\text{div}} = 2^{100},让“付费的” qℓ=1q_{\ell} = 1(即不消耗模数)。

后果推演:

  1. 分解阶段(Decompose): 你把密文分解为 ct=2100⋅ct^+ctˇct = 2^{100} \cdot \hat{ct} + \check{ct}。

    • 因为 qdiv=2100q_{\text{div}} = 2^{100},根据定义,你的低位部分(余数) ctˇ\check{ct} 的大小可以达到 21002^{100}。
    • 注意: 这个“低位”现在是个庞然大物,它本身就包含了巨大的数值。
  2. 乘法阶段(Tensor): 我们在做乘法时,扔掉了一项 ctˇ1×ctˇ2\check{ct}_1 \times \check{ct}_2(低位 ×\times 低位)。

    • 因为 ctˇ\check{ct} 大小是 21002^{100},所以这一项的大小约为 2100×2100=22002^{100} \times 2^{100} = 2^{200}。
    • 虽然 Lemma 4.2 的机制帮我们除了一次 qdivq_{\text{div}}(21002^{100}),但剩下的噪声依然高达: 22002100=2100\frac{2^{200}}{2^{100}} = 2^{100}
    • 现状: 你的计算结果里混入了一个大小为 21002^{100} 的随机噪声。
  3. 重缩放阶段(Rescale): 这是原本用来消除噪声的步骤。 公式是:除以 qℓq_{\ell}。 但因为你太贪心,设了 qℓ=1q_{\ell} = 1(为了不消耗模数)。

    • 结果: 2100÷1=21002^{100} \div 1 = 2^{100}。
    • 最终误差: 21002^{100}。

结局: 你的真实数据可能只有几十 bit,但误差高达 100 bit。有效信号完全被噪声淹没了,解密出来全是乱码。


2. 原理解析:为什么必须 50/50?

这里的核心制衡关系是:qdivq_{\text{div}} 制造噪声,而 qℓq_{\ell} 消除噪声。

数学上的硬性约束(来自 Theorem 4.8): 为了让最终误差 ≤1\le 1(或者可控),必须满足: 产生的噪声消除的能力≈qdivqℓ≤1\frac{\text{产生的噪声}}{\text{消除的能力}} \approx \frac{q_{\text{div}}}{q_{\ell}} \le 1 也就是说: qℓ≥qdivq_{\ell} \ge q_{\text{div}} (翻译:你花的钱,必须盖得住你制造的麻烦。)

寻找最优解:

这就是为什么 2502^{50} vs 2502^{50} 是极限。

总结:


第六部分:实验结果与相关工作

Q12: 1.5节和1.6节详解

问题:详细解释说明一下 1.5 节和 1.6 节。

回答:

这两个章节分别负责展示成果(1.5 节)和对比同行(1.6 节)。

简单来说:

我为你详细拆解如下:

12.1 第 1.5 节:Concrete examples(具体实例与实验结果)

这一节通过两个具体的应用场景,展示了 Mult2\text{Mult}^2(即 Double-CKKS)的威力。

场景 A:增加同态容量(Increased homomorphic capacity)

场景 B:提高精度与效率(Increased precision)—— 这是最亮眼的部分

总结 1.5:这个技术不仅仅是”省模数”,它通过省模数允许我们使用更小的 NN,从而实现了速度、存储、通信的全方位优化。

12.2 第 1.6 节:Related works(相关工作对比)

这里作者主要对比了两个容易混淆或相关的技术路线。

1. Gadget Decomposition(小工具分解)

这是以前在 GSW 方案或 CKKS 的 Key Switching 中常用的技术。

2. Module-CKKS / Module-LWE(基于模的 CKKS)

这是一个强有力的竞争对手。Module-CKKS 也是通过把密文变成向量(比如 3 个元素 (c0,c1,c2)(c_0, c_1, c_2))来尝试降低维度。

总结 1.6:

作者在说:“别误会,我们不是以前那种会让密钥变大的 Gadget 分解,也不是那种虽然维数小但模数依然很大的 Module-CKKS。我们是真·减半模数,综合性能最优。“


第七部分:CKKS 底层原理深入

Q13: 2.1节中的多个技术细节

问题:

  1. “The modulus Q is typically a product of RNS integers and, for the sake of efficiency, arithmetic modulo Q is performed in the RNS form whenever possible.” 似乎揭示了有些步骤是不能使用 RNS 表示的?
  2. 为什么规范嵌入中要选 5i5^i?是为了方便进行旋转操作?如果是这样的话,其背后的数学原理是什么?为什么非得是 5?
  3. 脚注中:“To minimize the numerical errors, it is useful to consider a different scaling factor for each multiplication level of the circuit to be homomorphically evaluated; for the sake of simplicity, we do not consider this optimization” 这是什么意思?实际代码实现中有考虑吗?
  4. 解密中,在不同的层级解密结果的精度是不一样的?层级越深,精度越低?
  5. 关于:“We assume that h is set so that the condition holds every time we use RS in this work.” 在 rescale 中,汉明权重 h 不是显然小于 Qℓ/qQ_\ell / q 吗?为啥要强调一下?
  6. 这段话是啥意思?freshly generated ciphertexts、BDec bound 是啥,干啥用的?

回答:

这几个问题依然非常硬核,涉及到同态加密底层数学原理和论文的严谨性表述。我逐条为你解析:

13.1 关于 “arithmetic modulo Q is performed in the RNS form whenever possible”

是的,这意味着某些步骤不能(或很难)直接在 RNS 下进行。

2. 为什么规范嵌入(Canonical Embedding)中要选 5j5^j?为什么是 5?

是为了旋转(Rotation/Galois Automorphism),背后的数学原理是分圆域的伽罗瓦群结构。

3. 关于“不同层级使用不同 scaling factor”的脚注

意思是:为了抵消模数不精确匹配带来的误差,工程上需要微调。

4. 解密结果的精度随层级变化吗?

是的,通常层级越深(剩余模数越小),相对精度越低(或保持不变但更危险)。

但这里要区分两个概念:

  1. 绝对噪声(Noise Norm): 随着乘法运算,噪声的绝对值在不断变大。
  2. 相对精度(有效比特位): Rescaling 的目的就是把“信号”和“噪声”同时缩小,试图维持相对精度。
    • 但是,Rescaling 本身引入了舍入误差。
    • 在 Mult2\text{Mult}^2 中,Low Part 的误差积累(如 Figure 1 所示)是随层数增加的。
    • 结论: 在这篇论文的方案里,如果不进行 Recombine,层级越深,累积的误差越大,解密出来的有效精度(Precision)确实越低。

5. 关于 h+1<Qℓ/qh+1 < Q_\ell/q 的假设

这是一个“活着”的底线条件。

6. 关于最后一整段话的含义

这段话非常重要,是在定义**“什么是解密”以及“正确性的标准”**。

A. Freshly generated ciphertexts & BDec bound

B. “Alternatively… definition as inner product… modulo Q0Q_0”

这是什么意思?

直接用底层模数模其他层级的密文,对吗?

C. “The first formulation is syntactically more convenient…”


Q14: BDecB_{Dec} 计算与规范嵌入

问题:

  1. BDecB_{Dec} 是怎么计算的?
  2. 规范嵌入(Canonical Embedding)中要选 5j5^j 为什么是 5?关于这个问题,有什么博客文章的推导过程或论述吗?

回答:

这两个问题直指同态加密(尤其是基于 Ring-LWE/RLWE 的方案如 CKKS、BFV、BGV)的底层核心。一个是关于正确性界限,一个是关于代数结构。

14.1 BDecB_{Dec} 是怎么计算的?

BDecB_{Dec} (Decryption Bound) 指的是为了保证解密正确,密文中所允许的最大噪声界限。

在 CKKS 中,解密公式通常是: m′=[⟨ct,sk⟩]Qm' = [\langle ct, sk \rangle]_{Q} 即先计算内积,然后模 QQ。

计算逻辑

为了能正确还原消息(或者近似消息),内积的结果 ⟨ct,sk⟩\langle ct, sk \rangle 在模 QQ 之前,绝对值不能超过 Q/2Q/2。否则发生模数回绕(Wrap-around),正数可能变成负数,或者数值产生巨大偏差。

数学上: ⟨ct,sk⟩=m+e+kQ\langle ct, sk \rangle = m + e + kQ 我们希望 k=0k=0。所以必须满足: ∣∣m+e∣∣∞<Q2|| m + e ||_\infty < \frac{Q}{2}

因此,BDecB_{Dec} 的计算公式通常很简单,就是当前模数的一半(减去消息本身的大小):

BDec≈Qℓ2−∣∣m∣∣∞B_{Dec} \approx \frac{Q_{\ell}}{2} - ||m||_\infty

在实际论文和参数设定中的意义:


2. 规范嵌入中为什么要选 5j5^j?为什么是 5?

这是一个纯粹的数论问题,源于分圆域(Cyclotomic Fields)的伽罗瓦群结构。

结论

选择 5 是为了构造模 2N2N 乘法群的一个生成元,从而能够遍历所有的槽(Slots)。

数学推导过程

  1. 背景:

    • 我们使用的环是 R=Z[X]/(XN+1)R = \mathbb{Z}[X] / (X^N + 1),其中 NN 是 2 的幂(N=2kN=2^k)。
    • 这对应于 MM-分圆域 Q(ζM)\mathbb{Q}(\zeta_M),其中 M=2N=2k+1M = 2N = 2^{k+1}。
    • CKKS 的 SIMD 操作(Batching)依赖于在这个域上的自同构(Automorphism)。
  2. 伽罗瓦群结构:

    • 这个域的伽罗瓦群 GG 同构于模 MM 的单位群(乘法群): G≅(Z/MZ)×G \cong (\mathbb{Z} / M\mathbb{Z})^\times
    • 我们需要知道这个群 (Z/2k+1Z)×(\mathbb{Z} / 2^{k+1}\mathbb{Z})^\times 长什么样。
  3. 核心定理(单位群结构): 对于 M=2k+1M = 2^{k+1} (且 k+1≥3k+1 \ge 3),模 MM 的乘法群不是循环群(没有单一生成元),而是同构于两个循环群的直积: (Z/2k+1Z)×≅C2×C2k−1(\mathbb{Z} / 2^{k+1}\mathbb{Z})^\times \cong C_2 \times C_{2^{k-1}}

    • C2C_2 (阶为 2 的子群): 由 -1 生成(对应于复共轭操作 τ:ζ→ζ−1\tau: \zeta \to \zeta^{-1})。
    • C2k−1C_{2^{k-1}} (阶为 N/2N/2 的子群): 需要一个元素,它在模 2k+12^{k+1} 下的阶是 2k−12^{k-1}。5 就是满足这个条件的最小整数。
  4. 为什么是 5?验证一下:

    • 我们需要一个数 gg,使得 gg 的幂次能生成形如 4k+14k+1 的所有数。
    • 尝试 3: 32=9≡1(mod8)3^2 = 9 \equiv 1 \pmod 8。3 的性质稍显复杂。
    • 尝试 5:
      • 51=55^1 = 5
      • 52=255^2 = 25
      • …
      • 数学归纳法可以证明:52k−2≡1+2k(mod2k+1)5^{2^{k-2}} \equiv 1 + 2^{k} \pmod{2^{k+1}},且 52k−1≡1(mod2k+1)5^{2^{k-1}} \equiv 1 \pmod{2^{k+1}}。
      • 这意味着 5 的阶确实是最大可能的 2k−12^{k-1}(即 N/2N/2)。
  5. 实际意义(Slot 映射):

    • 我们的明文槽向量(Slots)是通过 5j5^j 来索引的。
    • 如果我们只用 5 进行旋转(Rotation),我们可以访问到一半的槽(因为 5 生成了 N/2N/2 个元素)。
    • 另外一半槽通过复共轭(-1)来访问。
    • 公式: 任意一个位置索引 idxidx 都可以唯一表示为 idx=(−1)a⋅5b(mod2N)idx = (-1)^a \cdot 5^b \pmod{2N}。
    • 这就是为什么所有的同态旋转操作(Rotate)底层都是在算 5j5^j。

推荐的阅读资料与博客

关于这个问题,通常在介绍 HElib、SEAL 或 Ring-LWE 基础的硬核文档中会有详细推导。

  1. 推荐论文(圣经级):

    • Smart and Vercauteren, “Fully Homomorphic Encryption with Relatively Small Key and Ciphertext Sizes” (2010): 这篇论文详细讨论了 SIMD 操作和超立方体结构,确立了 55 作为生成元的标准。
    • Gentry, Halevi, Smart, “Homomorphic Evaluation of the AES Circuit” (2012): 这里面有关于 Batching 和 Galois Automorphism 的详细工程实现讨论。
  2. 推荐博客/讲义:

    • Vadim Lyubashevsky 的讲义 “A Toolkit for Ring-LWE Cryptography”: 这是一个非常好的综述,里面有关于环结构和自同构的数学解释。
    • Microsoft SEAL Manual: 虽然是说明书,但它的原理部分对 GaloisToolGaloisTool 的解释非常清晰,明确提到了 5j5^j 的映射关系。
    • 搜索关键词: 建议用 Google 搜索以下关键词组合:
      • Structure of multiplicative group modulo 2^k
      • Galois group of cyclotomic field 2^n
      • CKKS batching slot 5 generator
    • StackExchange: Math StackExchange 上有一个经典问题:“Why is the multiplicative group modulo 2n2^n not cyclic?” 里面会详细证明为什么生成元是 5 和 -1。

简而言之:选 5 是因为它是模 2n2^n 乘法群中那个大循环子群的标准生成元,能保证你“转”遍除了复共轭以外的所有位置。

BOB:1. 为什么系数选定在[-0.5 Q ,0.5Q],而不是[0,Q]? 2. Gadget decomposition was for example introduced to avoid modulus consumption in key switching. It comes in several flavours, including bit decomposition [5, 6], digit decomposition [13] and Residue Number System (RNS) based decomposition [3, 20, 21]. 为什么Gadget decomposition能够减少模数消耗??以及这几种Gadget decomposition具体是怎么做的?

这两个问题问得非常到位。第一个涉及到同态加密的数理基础(Norm),第二个涉及到同态加密中**密钥切换(Key Switching)**的核心技术。


1. 为什么系数选定在 [−0.5Q,0.5Q][-0.5Q, 0.5Q],而不是 [0,Q][0, Q]?

这不仅仅是一个表示习惯的问题,而是由 RLWE(Ring-LWE)问题的噪声(Error)机制决定的。

核心原因:为了正确衡量“噪声”的大小(最小绝对值)。

在同态加密中,安全性依赖于噪声 ee 是一个“很小”的值。解密成功的条件通常是:噪声的范数(Norm)小于模数的一半。

举个直观的例子

假设模数 Q=100Q = 100。 我们有一个噪声 e=−1e = -1。

  1. 在 [0,100][0, 100] 的表示法中: −1(mod100)=99-1 \pmod{100} = 99。 如果你直接看数值,9999 是一个很大的数,接近 QQ。在计算噪声大小时,如果你认为噪声是 9999,你会觉得解密肯定要失败了(因为 99>5099 > 50)。但实际上它代表的是 −1-1。

  2. 在 [−50,50][-50, 50] 的表示法中: −1(mod100)-1 \pmod{100} 被表示为 −1-1。 它的绝对值是 ∣−1∣=1|-1| = 1,这非常小,远小于 5050。这正确地反映了噪声的物理性质。

数学上的解释

总结: 选 [−0.5Q,0.5Q][-0.5Q, 0.5Q] 是为了让数值的绝对值直接等同于噪声的大小。如果你用 [0,Q][0, Q],你就需要额外一步逻辑:“如果 x>Q/2x > Q/2,则实际大小是 x−Qx-Q”。用对称区间可以省去这个麻烦,且符合误差分布(通常是高斯分布,均值为 0)的直觉。


2. 关于 Gadget Decomposition

这是一个在全同态加密(FHE)中非常经典的技术,主要用于优化 Key Switching (密钥切换) 操作。

2.1 为什么 Gadget decomposition 能够减少模数消耗?

首先要明白如果不做 Decomposition,Key Switching 有多“费模数”。

Key Switching 的痛点: 假设我们有一个密文 cc(系数很大,接近 QQ),我们需要把它乘以一个密钥转换公钥 KSKS(也很大,接近 QQ)。 直接相乘 c⋅KSc \cdot KS 会得到一个巨大的数,其大小约为 Q2Q^2。

Gadget Decomposition 的魔法: 它把大数 cc 拆成了一堆小数 cic_i。 c=c0+c1B+c2B2+…c = c_0 + c_1 B + c_2 B^2 + \dots 其中每个 cic_i 都很小(小于基数 BB)。

然后,我们不直接乘 KSKS,而是把 KSKS 也拆成针对每一位的“小公钥”向量: Key=(KS0,KS1,KS2… )\text{Key} = (KS_0, KS_1, KS_2 \dots) 其中 KSiKS_i 是针对 BiB^i 这一位的加密。

计算变成了: ∑(ci⋅KSi)\sum (c_i \cdot KS_i)

为什么省模数?

  1. 噪声变小了: 现在的噪声不再是 Q⋅eQ \cdot e,而是 ≈B⋅e\approx B \cdot e(因为 cic_i 很小)。
  2. 不需要大 PP: 因为噪声增幅很小(只是乘以了 BB),我们就不需要除以一个巨大的 PP 来压制噪声。
  3. 结论: 我们省下了那个巨大的辅助模数 PP 的空间,或者说让 PP 可以变得很小。这就叫做“减少了模数消耗”。

2.2 三种 Gadget Decomposition 的具体做法

这三种方法的本质区别在于**“基数(Base)”**的选择。假设我们要分解大整数 XX。

1. Bit Decomposition (位分解)

2. Digit Decomposition (数字/字分解)

3. RNS Decomposition (基于剩余数系统的分解)

总结表格:

分解类型基数 BB系数大小Key 大小噪声增长备注
Bit2{0,1}\{0, 1\}最大极小理论常用,太慢
Digit2k2^k (如 2202^{20})[0,B)[0, B)中等中等 (∝B\propto B)灵活可调
RNS质数 qiq_i≈qi\approx q_i较小较小工程标准,利于并行

BOB:2.3 CKKS bootstrapping详细解释

这一节(2.3 CKKS bootstrapping)介绍了 CKKS 方案中至关重要但代价昂贵的**“自举”(Bootstrapping, BTS)**过程。

为了让你彻底理解,我将通过**“为什么需要它”、“它的核心难点是什么”以及“具体四个步骤的逻辑”**三个层面来详细解释。


1. 为什么需要自举?(背景)

在 CKKS 中,每一次乘法运算后,为了维持密文的比例因子(Scaling Factor)不爆炸,我们必须执行 Rescale 操作。


2. 核心难点:那个“幽灵”整数 II

自举最难的地方在于:模数转换不是线性的。


3. 四个步骤详细解析

论文中提到的四个步骤(StC, ModRaise, CtS, EvalMod)是为了解决上述问题而设计的流水线。

步骤 1 & 3: StC 和 CtS (线性变换)

这是**系数域(Coefficients)和槽域(Slots)**之间的搬运工。

(注:具体的顺序取决于实现,通常是 ModRaise -> CtS -> EvalMod -> StC,或者 StC -> ModRaise -> EvalMod -> CtS,核心目的是为了在最合适的域做 EvalMod)

步骤 2: ModRaise (模数提升)

这是最简单粗暴的一步,也是引入问题的源头。

步骤 3: EvalMod (同态模约减)

这是最核心、最耗时的一步。


4. 总结:自举的全过程逻辑

  1. ModRaise: 强行提升模数,导致密文里混入了大整数倍的 Q0Q_0(即 m→m+Q0⋅Im \to m + Q_0 \cdot I)。
  2. CtS(变换): 为了方便计算,把这些混杂了误差的系数搬运到 SIMD 的槽里。
  3. EvalMod(清洗): 在槽里跑一个高次多项式,模拟“取模”操作,把 Q0⋅IQ_0 \cdot I 去掉,只剩下 mm。
  4. StC(归位): 把洗干净的数据搬回多项式系数里,得到最终干净的、大模数的新密文。

论文的切入点

作者在这里详细介绍自举是因为:

BOB:在3.2 Pair representation中: Definition 3.3. Let Ql be an element of the modulus chain 这句话中的Ql应该是l个素数的乘积?

不完全准确。准确地说,应该是 ℓ+1\ell + 1 个素数的乘积。

根据论文 第 4 页(2 PRELIMINARIES) 中对 Residue number system (RNS) 的定义:

“Let B={q0,…,qL}\mathcal{B} = \{q_0, \dots, q_L\} be a set of primes and Qℓ=∏i=0ℓqiQ_\ell = \prod_{i=0}^{\ell} q_i.”

这就意味着 QℓQ_\ell 是从下标 00 到下标 ℓ\ell 的所有素数相乘。

让我们详细拆解一下:

  1. 索引是从 0 开始的:

    • i=0,1,2,…,ℓi = 0, 1, 2, \dots, \ell。
    • 总个数是 (ℓ−0)+1=\ell+ 1(\ell - 0) + 1 = \textbf{\ell + 1} 个。
  2. 物理含义:

    • q0q_0 (Base Prime): 这是最底层的素数,通常用于最后的解密和编码,必须保留到最后。
    • q1,…,qℓq_1, \dots, q_\ell (Level Primes): 这些是用来支持 ℓ\ell 层乘法的“燃料”。每做一次 Rescaling,就会消耗掉最上面的一个素数。
  3. 在 Definition 3.3 中的语境:

    • 当定义说 QℓQ_\ell 时,它指的是**“当前密文处于第 ℓ\ell 层”**。
    • 这意味着这个密文还可以支持 ℓ\ell 次标准的 CKKS 乘法(或者在 Mult2\text{Mult}^2 语境下,通过组合可以支持更多)。
    • 它的模数大小(Bit-size)大约是:Bit(q0)+ℓ×Bit(qstep)\text{Bit}(q_0) + \ell \times \text{Bit}(q_{step})。

总结: QℓQ_\ell 包含 1 个基础素数 (q0q_0) 加上 ℓ\ell 个乘法素数 (q1…qℓq_1 \dots q_\ell),合计 ℓ+1\ell + 1 个素数。

BOB:对于定义3.1,其ct密文的表示并不是在RNS下的? 那为什么3.3中的ct密文又是在RNS下了?

这是一个关于论文理论抽象与工程实现之间如何衔接的极好问题。

简单直接的回答是:Definition 3.1 是纯数学层面的抽象定义(不关心底层表示),而 Definition 3.3 是将这个数学定义应用到 CKKS 模数链体系中的具体配置(这就隐含了 RNS 结构)。

我来为你详细拆解其中的逻辑转换:

1. Definition 3.1:抽象的数学操作

问题: “对于定义 3.1, 其 ct 密文的表示并不是在 RNS 下的?”

回答: Definition 3.1 是“表示中立”(Representation-agnostic)的。


2. Definition 3.3:结合 CKKS 模数链(隐含 RNS)

问题: “那为什么 3.3 中的 ct 密文又是在 RNS 下了?”

回答: 因为 Definition 3.3 将上述数学定义“实例化”到了 CKKS 的上下文里,而 CKKS 的模数链 QℓQ_\ell 本质上就是 RNS 结构的。


3. 关键点:当 3.1 遇见 3.3(RNS 技巧)

这才是你感到困惑的核心——作者是如何把“难算的 3.1”变成“好算的 3.3”的?

当我们将 Definition 3.1 应用于 RNS 形式的密文,并且巧妙地选择除数 q=qdivq = q_{\text{div}} 为 RNS 的其中一个模数时,奇迹发生了:

  1. 对于 Remqdiv(ct)\text{Rem}_{q_{\text{div}}}(ct) (取余):

    • 在 RNS 中,密文被表示为 (c(modq0),…,c(modqdiv))(c \pmod{q_0}, \dots, c \pmod{q_{\text{div}}})。
    • 如果我们想求 c(modqdiv)c \pmod{q_{\text{div}}},在 RNS 里这不需要任何计算!直接把对应 qdivq_{\text{div}} 的那个分量拿出来就行了。
    • 这就是为什么在 3.3 的语境下,操作变得极其高效。
  2. 对于 Quoqdiv(ct)\text{Quo}_{q_{\text{div}}}(ct) (取商):

    • 公式是 (ct−余数)/qdiv(ct - \text{余数}) / q_{\text{div}}。
    • 在 RNS 中,这等价于**Rescaling(重缩放)**操作:扔掉 qdivq_{\text{div}} 对应的分量,然后在其他分量上乘以 qdiv−1q_{\text{div}}^{-1}。
    • 这正是 CKKS 标准的 RNS-Rescaling 算法。

总结

所以,并没有矛盾。3.1 是通用的数学描述,3.3 是基于 RNS 架构的高效实现配置。

BOB:Before introducing our algorithm, we recall a classical technique used to double precision in fixed-point arithmetic.

这里的fixed-point arithmetic是什么东西?

Fixed-point arithmetic(定点数算术/定点运算) 是一种在计算机中表示和计算小数(实数)的方法。

简单来说,它的核心思想是:把小数放大成整数来存,计算完再缩小回去。

为了让你完全理解这句“classical technique”,我们需要拆解一下定点数的原理以及它和这篇论文的关系。


1. 什么是定点数(Fixed-Point)?

计算机里通常只擅长处理整数。为了处理小数(比如 3.143.14),有两种主流方法:浮点数(Floating-point)和定点数(Fixed-point)。

通俗例子:钞票

想象一下,你不想处理“12.34 元”这样的小数。 你决定:所有的钱都以“分”为单位(Fixed-point)。

这就是定点数算术:用整数运算模拟小数运算。


2. 为什么论文提到“经典技术”?

论文中说:“recall a classical technique used to double precision…”(回顾一种在定点算术中用于双倍精度的经典技术)。

这个“经典技术”指的是:当数字太大,寄存器装不下时,如何把大数拆成两半来做乘法。

场景还原

假设你的计算器(或 CPU)很老旧,它只能做 2 位数的乘法(比如 99×9999 \times 99),如果结果超过 4 位数就会溢出。 现在你需要计算两个 4 位数 的乘法:1234×56781234 \times 5678。怎么做?

经典技术(拆分法): 你把数字拆成 高位(High) 和 低位(Low)。 设定拆分点(基数) B=100B = 100。

现在计算 X×YX \times Y: X⋅Y=(12⋅100+34)×(56⋅100+78)X \cdot Y = (12 \cdot 100 + 34) \times (56 \cdot 100 + 78)

展开这个式子(分配律): =(12×56)⏟高×高⋅10000+(12×78+34×56)⏟交叉项⋅100+(34×78)⏟低×低= \underbrace{(12 \times 56)}_{\text{高}\times\text{高}} \cdot 10000 + \underbrace{(12 \times 78 + 34 \times 56)}_{\text{交叉项}} \cdot 100 + \underbrace{(34 \times 78)}_{\text{低}\times\text{低}}

这有什么用? 这里的每一次小乘法(比如 12×5612 \times 56)都是 2 位数乘法,你的老旧计算器完全算得过来!算完之后,再通过移位(乘以 10000 或 100)和加法把结果拼起来。

这就是论文的核心灵感:


3. 定点数与 CKKS 的关系

CKKS 本质上就是一个定点数加密系统。

总结: 文中提到的“fixed-point arithmetic”指的就是这种通过放大因子用整数模拟小数的机制。而那句“classical technique”指的是将大整数拆分为高低位进行分块乘法的算法,这正是 Mult2\text{Mult}^2 算法的原型。

BOB:在RNS-CKKS下,密文的解密是怎样的?假设在l层进行解密,那么ct_l和sk进行内积,得到的不是一个长度为l的向量吗?解密消息在哪里?

这是一个非常棒的问题!你敏锐地察觉到了 RNS(剩余数系统)表示法 和我们直觉中的 大整数/多项式 之间的“视觉差异”。

简单回答你的问题: 是的,内积的结果确实是一个“向量”(或者说是一组小整数的列表)。 但是,这个向量整体“就是”那个解密后的消息多项式。你之所以没看到“消息”,是因为它还穿着 RNS 的“马甲”。

为了拿到真正的明文消息,你需要进行最后一步操作:CRT 组合(Chinese Remainder Theorem Reconstruction)。

我为你详细拆解这个过程:


1. 解密的第一步:RNS 域内的内积

假设我们在第 ℓ\ell 层解密。

执行内积 ⟨ct,sk⟩\langle ct, sk \rangle: 这个操作是**分量并行(Component-wise)**进行的。对于每一个 RNS 分量 ii(从 00 到 ℓ\ell),我们计算: m(i)=c0(i)+c1(i)⋅s(i)(modqi)m^{(i)} = c_0^{(i)} + c_1^{(i)} \cdot s^{(i)} \pmod{q_i}

结果: 你确实得到了一个长度为 ℓ+1\ell+1 的向量(列表): Decrypted_RNS=[m(0),m(1),…,m(ℓ)]\text{Decrypted\_RNS} = [m^{(0)}, m^{(1)}, \dots, m^{(\ell)}]

这里的每一个 m(i)m^{(i)} 都是一个多项式,但它的系数都很小(在 qiq_i 范围内)。


2. 消息在哪里?(CRT 重建)

这时候你可能会问:“我的消息 mm 在哪?是 m(0)m^{(0)} 吗?还是 m(1)m^{(1)}?” 都不是,也都是。

这就像我问你:“数字 10 在哪里?” 如果我们在 RNS 基 {3,5}\{3, 5\} 下表示 10:

数字 10 既不是 1,也不是 0,而是 (1, 0) 这一对数所唯一确定的那个大整数。

CKKS 的情况同理: 真正的解密后多项式 M(X)M(X)(也就是 m+em + e),是一个系数巨大的多项式。 上述向量 [m(0),…,m(ℓ)][m^{(0)}, \dots, m^{(\ell)}] 就是这个巨大多项式 M(X)M(X) 在模 QℓQ_\ell 下的 RNS 表示。

为了看到“消息”,你需要做 CRT 重建(CRT Composition): 利用中国剩余定理公式,把这些小多项式拼回去: M(X)=∑i=0ℓ(m(i)(X)⋅Q^i⋅Q^i−1)(modQℓ)M(X) = \sum_{i=0}^{\ell} \left( m^{(i)}(X) \cdot \hat{Q}_i \cdot \hat{Q}_i^{-1} \right) \pmod{Q_\ell}


3. 解密的最后一步:解码(Decode)

现在你手上有了一个大整数多项式 M(X)M(X)。 它的物理意义是: M(X)≈Δ⋅m(X)+NoiseM(X) \approx \Delta \cdot m(X) + \text{Noise}

接下来的步骤(通常在 CPU 的大数库或浮点库中完成):

  1. 系数转换: 把 M(X)M(X) 的系数从模 QℓQ_\ell 域([0,Qℓ)[0, Q_\ell))映射到有符号整数域([−Qℓ/2,Qℓ/2)[-Q_\ell/2, Q_\ell/2))。
  2. 除以缩放因子: 每个系数除以 Δ\Delta。 Result(X)≈m(X)\text{Result}(X) \approx m(X)
  3. Canonical Embedding 逆变换: 把这个多项式 m(X)m(X) 映射回由复数组成的向量(Slots)。

这就是你最终看到的明文消息。


总结

(注:在高效的代码实现如 HEaaN/SEAL 中,为了速度,步骤 2 和 3 往往是合并优化的,直接通过算法从 RNS 转换到浮点数,而不显式生成那个巨大的 BigInt 多项式,但逻辑原理就是上面这样。)

BOB:4.1 Tools的 Lemma 4.2 ,我没看懂,向我 详细解释

Lemma 4.2 是整篇论文中 Mult2\text{Mult}^2 算法的数学基石。

它主要证明了两件事:

  1. 正确性: 我们定义的 Tensor2\text{Tensor}^2 运算(扔掉低位乘低位),在数学上确实等价于把两个大密文相乘后除以了 qdivq_{\text{div}}。
  2. 误差可控: 那个被我们扔掉的项(低位 ×\times 低位),不仅是可以被忽略的,而且它的具体的误差形式是非常清晰的。

我来一步步为你拆解这个引理的推导过程。


1. 符号与背景设定

首先,我们先把复杂的符号简化一下,方便理解:


2. Tensor2\text{Tensor}^2 做了什么?

论文中定义的 Tensor2\text{Tensor}^2 操作生成了一个新的密文对 (NewHigh,NewLow)(\text{NewHigh}, \text{NewLow}):

现在,我们把这个 Tensor2\text{Tensor}^2 的结果**重组(RCB)**回去看看它代表什么数: ResultTensor2=qdiv⋅NewHigh+NewLow\text{Result}_{\text{Tensor}^2} = q_{\text{div}} \cdot \text{NewHigh} + \text{NewLow} =qdiv(X^Y^)+(X^Yˇ+XˇY^)= q_{\text{div}} (\hat{X}\hat{Y}) + (\hat{X}\check{Y} + \check{X}\hat{Y})


3. Lemma 4.2 的核心等式解析

Lemma 4.2 的第一个公式本质上就是把 “标准乘法” 和 “Tensor2\text{Tensor}^2 乘法” 放在一起比较。

我们把上面的“标准乘法展开式”变形一下: X⋅Y=qdiv[qdiv(X^Y^)+(X^Yˇ+XˇY^)]+(XˇYˇ)X \cdot Y = q_{\text{div}} \left[ q_{\text{div}} (\hat{X}\hat{Y}) + (\hat{X}\check{Y} + \check{X}\hat{Y}) \right] + (\check{X}\check{Y})

仔细看中括号里的内容,那正是 ResultTensor2\text{Result}_{\text{Tensor}^2}!

所以,就得到了 Lemma 4.2 的核心逻辑等式: X⋅Y=qdiv⋅(Tensor2的结果)+(扔掉的低位乘积)X \cdot Y = q_{\text{div}} \cdot (\text{Tensor}^2 \text{的结果}) + (\text{扔掉的低位乘积})

对应论文中的公式: (ct1⊗ct2)⋅s2=qdiv⋅(RCB(Tensor2)⋅s2)+(ctˇ1s)(ctˇ2s)(ct_1 \otimes ct_2) \cdot s^2 = q_{\text{div}} \cdot (\text{RCB}(\text{Tensor}^2) \cdot s^2) + (\check{ct}_1 s)(\check{ct}_2 s) (注:乘 s2s^2 是因为 Tensor 后密文变成了 3 维,需要用 (1,s,s2)(1, s, s^2) 解密)

这个等式告诉我们两件事:

  1. 隐式除法: Tensor2\text{Tensor}^2 的结果,大约等于 (X⋅Y)/qdiv(X \cdot Y) / q_{\text{div}}。这就解释了为什么我说它自带“隐式除法”功能。
  2. 误差来源: 剩下的误差项就是 XˇYˇ\check{X}\check{Y}(即论文中的 (ctˇ1⋅sk)(ctˇ2⋅sk)(\check{ct}_1 \cdot sk)(\check{ct}_2 \cdot sk))。

4. 误差界限(Bound)解析

Lemma 4.2 的后半部分是在算:这个被扔掉的误差到底有多大?

论文给出的界限是: Error≤NMˇ2qdiv\text{Error} \le \frac{N \check{M}^2}{q_{\text{div}}}

我们来推导一下为什么是除以 qdivq_{\text{div}}:

  1. 绝对误差: 被扔掉的项是 Xˇ⋅Yˇ\check{X} \cdot \check{Y}。

    • Xˇ\check{X} 的大小约为 Mˇ\check{M}。
    • 所以绝对误差约为 Mˇ2\check{M}^2。
  2. 相对误差(归一化):

    • 请注意,Tensor2\text{Tensor}^2 的输出结果(即那个商)是处于被“除以了 qdivq_{\text{div}}”的尺度上的。
    • 为了衡量误差对结果的影响,我们必须把绝对误差也除以 qdivq_{\text{div}}。
    • 想象一下:原本误差是 100。但因为你的结果缩小了 100 倍,所以相对于结果来说,这个误差相当于只剩 1 了。
  3. 结论: 所以,最终对解密结果的影响(Infinity Norm)被控制在 Mˇ2qdiv\frac{\check{M}^2}{q_{\text{div}}} 这个量级。

    • 只要我们选的 qdivq_{\text{div}} 足够大(比 Mˇ2\check{M}^2 大),这个误差就会小于 1,从而被忽略不计。

总结

Lemma 4.2 是在用数学语言说:

“兄弟们,我们把 (Ahigh⋅Blow+… )(A_{high} \cdot B_{low} + \dots) 这些项算出来,拼成一个新的密文。虽然我们扔掉了 Low×LowLow \times Low 这一项,但根据公式推导,我们得到的新密文,在数学上严格等于 ‘(原数乘积 - 扔掉的垃圾) 除以 qdivq_{\text{div}}’。只要垃圾足够小,我们算出来的就是对的,而且还顺便帮大家做了一次免费的除法!”

BOB:为什么要保证这个条件?以及这个条件是怎么推导出来的? Alt text

这是一个非常核心的问题,涉及到同态加密中**“模运算正确性”**的边界条件。

简单来说,这个条件是为了保证**“计算结果没有发生溢出(Wrap-around)”**。如果结果溢出了模数 QℓQ_\ell,那么原本在整数环 RR 上成立的等式,在模 QℓQ_\ell 的密文空间里就不再成立了。

我来为你详细拆解这个条件的推导过程和背后的逻辑。


1. 为什么要保证这个条件?(防止溢出)

核心逻辑: Lemma 4.2 试图证明一个等式: Standard_Tensor=Decomposed_Tensor+Error\text{Standard\_Tensor} = \text{Decomposed\_Tensor} + \text{Error} 这个等式是在 整数多项式环 RR 上讨论的(即没有取模)。

然而,CKKS 的密文是存储在 模 QℓQ_\ell 的环上的。

结论: 必须保证乘法结果的**绝对值(范数)**始终小于 Qℓ/2Q_\ell/2,这样模运算的结果才等于真值,引理中的数学推导才能在密文世界里生效。


2. 这个条件是怎么推导出来的?

让我们像剥洋葱一样拆解公式: N(M^qdiv+Mˇ)2<Qℓ/2N(\hat{M}q_{\text{div}} + \check{M})^2 < Q_\ell/2

第一步:估算单个明文的大小

我们在做乘法 m1×m2m_1 \times m_2。先看 m1m_1 有多大。 根据分解定义: m≈qdiv⋅m^+mˇm \approx q_{\text{div}} \cdot \hat{m} + \check{m}

根据三角不等式,明文 mm 的最大范数大约是: ∥m∥∞≈qdiv∥m^∥∞+∥mˇ∥∞≤qdivM^+Mˇ\|m\|_\infty \approx q_{\text{div}} \|\hat{m}\|_\infty + \|\check{m}\|_\infty \le q_{\text{div}}\hat{M} + \check{M}

第二步:估算乘积的大小

现在计算两个这样的明文相乘:mprod=m1⋅m2m_{prod} = m_1 \cdot m_2。

在多项式环 R=Z[X]/(XN+1)R = \mathbb{Z}[X]/(X^N+1) 中,两个多项式相乘,其系数会发生膨胀。 膨胀公式: 如果 ∥a∥∞≤A\|a\|_\infty \le A 且 ∥b∥∞≤B\|b\|_\infty \le B,那么 ∥a⋅b∥∞≤N⋅A⋅B\|a \cdot b\|_\infty \le N \cdot A \cdot B。 (注:这里 NN 是多项式的度,比如 2152^{15}。这是因为卷积运算会把 NN 个项累加起来)

将第一步的界限代入: ∥m1⋅m2∥∞≤N⋅(qdivM^+Mˇ)⋅(qdivM^+Mˇ)\|m_1 \cdot m_2\|_\infty \le N \cdot (q_{\text{div}}\hat{M} + \check{M}) \cdot (q_{\text{div}}\hat{M} + \check{M}) ∥m1⋅m2∥∞≤N(qdivM^+Mˇ)2\|m_1 \cdot m_2\|_\infty \le N (q_{\text{div}}\hat{M} + \check{M})^2

第三步:限制在模数范围内

为了保证计算 m1⋅m2m_1 \cdot m_2 时不发生模 QℓQ_\ell 的溢出,这个乘积的范数必须小于模数的一半:

乘积的大小<Qℓ2\text{乘积的大小} < \frac{Q_\ell}{2}

即: N(qdivM^+Mˇ)2<Qℓ/2\mathbf{N (q_{\text{div}}\hat{M} + \check{M})^2 < Q_\ell/2}


3. 物理意义解读

这个公式实际上是在告诉我们:当前的模数 QℓQ_\ell 是否足够大,能够容纳得下这次乘法的结果?

如果这个条件不满足,说明模数太小了(或者明文太大了),乘法结果把容器撑爆了。这就意味着我们在做 Tensor\text{Tensor} 这一步时,还没来得及做 Rescaling,数据就已经损坏了。

所以,这是一个安全性检查(Sanity Check),确保在执行 Mult2\text{Mult}^2 的第一步(Tensor)时,基础的 CKKS 乘法逻辑是成立的。

BOB:这个图中的left and right hand sides of the eqution指的是什么

这张图里提到的“equation in the lemma statement”(引理声明中的等式),指的就是 Lemma 4.2 中给出的那个核心等式。

为了让你看清楚,我把 Lemma 4.2 中的那个等式写出来(对应论文第 7 页):

(ct1⊗ct2)⋅(1,s,s2)⏟Left Hand Side (LHS)≡qdiv⋅(RCB(… ))⋅(1,s,s2)+(ctˇ1s)(ctˇ2s)⏟Right Hand Side (RHS)(modQℓ)\underbrace{(ct_1 \otimes ct_2) \cdot (1, s, s^2)}_{\text{Left Hand Side (LHS)}} \equiv \underbrace{q_{\text{div}} \cdot (\text{RCB}(\dots)) \cdot (1, s, s^2) + (\check{ct}_1 s)(\check{ct}_2 s)}_{\text{Right Hand Side (RHS)}} \pmod{Q_\ell}

这里的 Left and Right 分别指代:

1. Left Hand Side (LHS): 标准乘法的解密值

LHS=(ct1⊗ct2)⋅(1,s,s2)\text{LHS} = (ct_1 \otimes ct_2) \cdot (1, s, s^2)

2. Right Hand Side (RHS): 分解乘法的重组值

RHS=qdiv⋅(… )+(ctˇ1⋅sk)(ctˇ2⋅sk)\text{RHS} = q_{\text{div}} \cdot (\dots) + (\check{ct}_1 \cdot sk)(\check{ct}_2 \cdot sk)

这里的逻辑链条

  1. 原本的等式是“模 QℓQ_\ell 同余”: Lemma 4.2 最初只说左右两边在 (modQℓ)\pmod{Q_\ell} 下是相等的。 LHS≡RHS(modQℓ)\text{LHS} \equiv \text{RHS} \pmod{Q_\ell}

  2. 我们需要的是“真相等”: 为了证明误差分析是正确的,我们需要这个等式在整数环 RR 上直接成立(即 A=BA=B,而不仅仅是 A≡BA \equiv B)。这对于后续把 qdivq_{\text{div}} 除过去至关重要。

  3. 条件的作用: 图中提到的条件 N(M^qdiv+Mˇ)2<Qℓ/2N(\hat{M}q_{\text{div}} + \check{M})^2 < Q_\ell/2 就是在说:

    “大家看,LHS(也就是 m1⋅m2m_1 \cdot m_2)的最大可能值都比模数的一半要小。”

    • 既然 LHS <Qℓ/2< Q_\ell/2(没有溢出)。
    • 且 RHS 在数学上等于 LHS。
    • 那么 RHS <Qℓ/2< Q_\ell/2(也没有溢出)。
    • 结论: 既然两边都没溢出,且模 QℓQ_\ell 相等,那它们就是真的相等(holds over RR)。

总结: Left/Right Hand Sides 指的就是 “明文真值(LHS)” 和 “算法算出来的还原值(RHS)”。这句话是在确保这两个值都没有因为太大而撑爆模数容器。

BOB:Lemma 4.4.中的这段话是啥意思? Alt text

这段话是在解释为什么作者设计的 Relin2\text{Relin}^2 算法比一种“直观但幼稚”的方法更优秀。

核心争论点在于:做一次重线性化(Relinearization),到底会不会让你的密文模数变小(即“消耗模数”)?

我为你拆解这段话的逻辑:


1. 那个“幼稚的方法” (The Naive Approach) 是什么?

逻辑是这样的:既然我们已经把密文拆成了 (ct^,ctˇ)(\hat{ct}, \check{ct}),那我们要对它做 Relin,最简单的思路不就是:

  1. 拼回去 (RCB): 先把两部分合体,变回一个大密文 CTCT。
  2. 做标准 Relin: 对这个大密文做普通的 CKKS 重线性化。
  3. 再拆开 (DCP): 把做完的结果再拆成新的 (ct^′,ctˇ′)(\hat{ct}', \check{ct}')。

即公式:DCPqdiv∘Relin∘RCBqdiv(CT)\text{DCP}_{q_{\text{div}}} \circ \text{Relin} \circ \text{RCB}_{q_{\text{div}}}(CT)。

2. 为什么幼稚的方法会“消耗模数”?

作者指出:“Note that this consumes modulus… DCP decreases the modulus”。


3. 作者的方法 (Relin2\text{Relin}^2) 为什么“不消耗模数”?

作者的策略是:先乘后除,抵消损耗。

回顾 Relin2\text{Relin}^2 的公式第一项: DCPqdiv(Relin(qdiv⋅ct^))\text{DCP}_{q_{\text{div}}}(\text{Relin}(\mathbf{q_{\text{div}} \cdot \hat{ct}}))

这里的操作流程是:

  1. 预先放大 (Multiply by qdivq_{\text{div}}):

    • 作者没有直接对 ct^\hat{ct} 做 Relin,而是先把它乘了 qdivq_{\text{div}}。
    • 在代数上,这意味着这个多项式的数值扩大了 qdivq_{\text{div}} 倍。
    • 在模数意义上,你可以理解为我们虚拟地把它提升到了一个更大的模数空间(或者是把原本属于 qdivq_{\text{div}} 这一层的“配额”给补上了)。
  2. 执行 DCP (Divide by qdivq_{\text{div}}):

    • DCP 操作虽然会物理上“消耗”掉 qdivq_{\text{div}} 这个模数因子(执行除法)。
    • 但是! 因为我们第一步已经先乘了 qdivq_{\text{div}}。
    • 所以:(原模数 ×qdiv\times q_{\text{div}})÷qdiv=\div q_{\text{div}} = 原模数。

4. 总结这段话的意思

结论: 这段话在吹嘘 Relin2\text{Relin}^2 的设计巧妙之处:它通过数学上的预处理,使得这一步操作变成了**“模数免费”**的操作,从而把宝贵的模数节省下来留给 Rescaling(RS2\text{RS}^2)步骤使用。

BOB:对ct^乘以q_div要怎么实现?

这是一个非常切中要害的工程实现问题!

在 RNS-CKKS 的架构下,实现“将密文 ct^\hat{ct} 乘以 qdivq_{\text{div}}” 实际上是一个**“模数提升(Modulus Raising)”的特例,而且是一个非常简单且计算代价极低**的特例。

我们假设当前的 ct^\hat{ct} 处于模数 QℓQ_\ell 下,RNS 基为 {q0,q1,…,qℓ}\{q_0, q_1, \dots, q_\ell\}。 我们的目标是获得一个处于模数 Qℓ⋅qdivQ_\ell \cdot q_{\text{div}} 下的新密文。

具体实现步骤如下:

1. 对现有的 RNS 分量:标量乘法

对于当前已经在 QℓQ_\ell 中的每一个 RNS 模数 qiq_i (i=0…ℓi=0 \dots \ell):

2. 对 qdivq_{\text{div}} 分量:直接置零(关键步骤)

既然我们要把模数扩充到包含 qdivq_{\text{div}},我们需要确定新密文在 qdivq_{\text{div}} 这个模数下的 RNS 分量是多少。


为什么说这“不消耗模数”?

结合上述实现,我们再看回头看 Relin2\text{Relin}^2 的全过程:

  1. 乘 qdivq_{\text{div}}(Raise Modulus):

    • 现有分量乘以常数。
    • 新增一个 qdivq_{\text{div}} 分量,填 0。
    • 状态: 密文现在拥有了 Qℓ⋅qdivQ_\ell \cdot q_{\text{div}} 的模数空间。
  2. 做 Relin(Key Switching):

    • Key Switching 通常需要产生大模数密文,并在最后进行除法(Rescale)。
    • 在这个扩充后的模数空间里做 Relin。
  3. 做 DCP(分解/除法):

    • DCP 的本质是“Rescaling by qdivq_{\text{div}}”。
    • 操作: 扔掉 qdivq_{\text{div}} 这个 RNS 分量(刚才那个 0 分量,或者经过 Relin 变成非 0 的分量),然后把其他分量乘以 qdiv−1q_{\text{div}}^{-1}。
    • 状态: 密文回到了 QℓQ_\ell 的模数空间。

总结: 实现“乘以 qdivq_{\text{div}}”之所以简单且高效,是因为它是正好乘以模数本身。在 RNS 系统中,这等价于**“在原基础上乘常数” + “在扩展位上填 0”**。这避免了常规模数提升(Modulus Switching Up)中极其昂贵的通用基转换计算。

BOB:我有个疑问,在这篇论文的实现里,素数模数是不是比一般的rns-ckks中的素数模数小一半?然后模数数量要多一倍?

这是一个非常敏锐的观察!你的直觉触及到了工程实现的核心,但实际情况和你推测的刚好相反。

简短的回答是:

  1. 素数模数的大小(Bit-size): 并没有变小一半,它们保持不变(通常都是 50~60 bits,为了适配 64 位机器字长)。
  2. 模数的数量(Number of primes): 并不是多一倍,而是少了一倍(对于同等精度的每一层乘法而言)。

让我用论文中的 100-bit 高精度 案例(Table 3)来为你详细拆解这笔账。


1. 为什么素数模数大小不变?

在 RNS-CKKS 的工程实现(如 HEaaN, SEAL)中,为了追求 CPU 运算速度,每一个 RNS 素数 qiq_i 都必须小于 64 bits(通常选 50-60 bits)。这样所有的模运算都可以直接用 CPU 指令完成,不需要调用慢速的大数库。


2. 怎么实现 100-bit 精度?(Standard vs Mult2^2)

假设我们的目标是 Scaling Factor Δ≈2100\Delta \approx 2^{100}(即每次乘法后需要除以 21002^{100})。

A. 标准 CKKS 的做法(“拼单”)

因为一个素数最大只有 60 bits,凑不够 100 bits。 所以,标准 CKKS 必须用两个素数来凑一层。

B. Mult2^2 的做法(“单干”)

Mult2^2 的魔法在于:它只需要“物理消耗”一半的模数(即 ≈250\approx 2^{50}),另一半(qdiv≈250q_{\text{div}} \approx 2^{50})通过算法结构“白嫖”了。


3. 最终对比:谁用的模数更多?

让我们看论文 Table 3 的实验数据(针对 8 层乘法,100-bit 精度):

特性标准 CKKS (Standard)Mult2^2 (Our work)
单层所需的模数位宽100 bits50 bits
素数配置 (RNS Primes)(50 × 2)
(每层需要 2 个 50-bit 素数拼起来)
60 × 1
(每层只需要 1 个 60-bit 素数)
8 层所需的素数总数≈16\approx 16 个≈8\approx 8 个
最大总模数 (log⁡Qmax\log Q_{max})1000 bits680 bits

(注:680 bits 中包含了基础模数和其他开销,所以不是简单的 8×60=4808 \times 60 = 480,但明显远小于 1000)

结论

你的推测逻辑是:“如果要省一半模数,是不是因为把模数切小了?”

事实是: 模数块(积木)的大小没变(都是 50-60 bit 的标准砖头)。

所以,Mult2^2 实际上是大幅减少了模数的数量和总大小,这正是它能提升速度和减小密文尺寸的根本原因。

BOB: 但是密文的数量加倍了,对吧? 以及q_div的大小和素数模数的大小相加应该等于scale?

你的这两个理解都非常准确!

你已经完全看透了这篇论文在“宏观架构”和“参数微观搭配”上的核心逻辑。


1. 关于“密文数量加倍”

是的,逻辑上密文数量确实加倍了。

但是!为什么论文还说它能减小密文大小(Storage Size)呢?

这是因为虽然“数量”翻倍了,但每个“单体”变轻了很多。这里有一个**“以数量换空间”**的博弈:

  1. 模数变小(log⁡Q\log Q):
    • 标准密文的模数 Q≈1000Q \approx 1000 bits。
    • Mult2\text{Mult}^2 密文的模数 Q≈680Q \approx 680 bits。
    • 单体变小了约 30%。
  2. 环维数减半(NN): 这是最关键的!
    • 因为模数变小了,根据 LWE 安全性估算,我们可以把环维数 NN 从 2162^{16} 降到 2152^{15}。
    • 这直接让多项式的系数个数少了一半。

总账计算(以 Table 3 为例):

结论: 虽然我们手里拿了两个密文,但因为每个密文都变得非常“袖珍”(维度减半、模数减小),所以总的存储空间反而变小了(从 14.8MB 降到了 5.08MB)。


2. 关于“qdivq_{\text{div}} 和素数模数的大小相加等于 Scale”

完全正确! (准确地说是比特数(Bit-size)相加)。

这里的数学关系是乘法关系,但在工程参数(Bit-size)上表现为加法关系。

物理意义的对应:

  1. qdivq_{\text{div}} (约 50 bits):

    • 这是**“隐式 Scaling”**。
    • 在 Tensor2\text{Tensor}^2 步骤中,通过扔掉低位和提取高位,数据在数学上被缩小了 qdivq_{\text{div}} 倍。
    • 这部分缩放是不消耗模数链的。
  2. qℓq_{\ell} (约 50 bits):

    • 这是**“显式 Scaling”**。
    • 在 RS2\text{RS}^2 步骤中,通过物理除法,数据被缩小了 qℓq_{\ell} 倍。
    • 这部分缩放是消耗模数链的(也就是我们实打实用的那个 RNS 素数)。

总结: 你理解得非常透彻。Mult2\text{Mult}^2 本质上就是把一个大的 Scaling Factor(比如 100 bits)拆成了两半:

BOB:4.2的Modulus consumption.中,为什么the size of Mˇ是C ^ 2 * q_div?

这是一个非常细致的问题!你可能是在阅读论文第 8 页 Modulus consumption 这一段时,对其中的公式推导产生了疑惑。

实际上,这里存在一个阅读上的陷阱(或者是论文原文的一个笔误/简写)。

准确地说:Mˇ\check{M} 的大小(Size)应该是 ≈C⋅qdiv\approx C \cdot q_{\text{div}},而不是 C2⋅qdivC^2 \cdot q_{\text{div}}。

那个出现的 C2⋅qdivC^2 \cdot q_{\text{div}} 其实是误差项公式计算后的结果。

我来为你详细还原这个推导过程:

1. Mˇ\check{M} 的真实定义

首先,Mˇ\check{M} 是低位部分(Low Part) ctˇ\check{ct} 的范数上界。 根据分解的定义(Decomposition): ctˇ=Remqdiv(ct)\check{ct} = \text{Rem}_{q_{\text{div}}}(ct) 也就是 ctct 除以 qdivq_{\text{div}} 的余数。

在数学上,余数绝对不能超过除数。所以: ∥ctˇ∥≤qdiv2\|\check{ct}\| \le \frac{q_{\text{div}}}{2} 因此,我们可以认为 Mˇ\check{M} 的大小主要由 qdivq_{\text{div}} 决定。 我们可以记作: Mˇ≈C⋅qdiv\check{M} \approx C \cdot q_{\text{div}} (其中 CC 是一个接近 0.50.5 或稍大一点的常数,考虑到噪声增长)。

2. 为什么出现了 C2⋅qdivC^2 \cdot q_{\text{div}}?

这个项出现在误差项的计算中。

回顾 Theorem 4.8 和 Lemma 4.2,Mult2\text{Mult}^2 引入的主要误差项是: Error≈Mˇ2qdiv\text{Error} \approx \frac{\check{M}^2}{q_{\text{div}}} (注:这里忽略了多项式膨胀系数 NN 和分母中的 qℓq_\ell)

让我们把 Mˇ≈C⋅qdiv\check{M} \approx C \cdot q_{\text{div}} 代入这个公式:

Error≈(C⋅qdiv)2qdiv\text{Error} \approx \frac{(C \cdot q_{\text{div}})^2}{q_{\text{div}}} =C2⋅qdiv2qdiv= \frac{C^2 \cdot q_{\text{div}}^2}{q_{\text{div}}} =C2⋅qdiv= \mathbf{C^2 \cdot q_{\text{div}}}

真相大白: 文中提到的 C2⋅qdivC^2 \cdot q_{\text{div}} 并不是 Mˇ\check{M} 本身的大小,而是**Mˇ2\check{M}^2 除以 qdivq_{\text{div}} 之后得到的结果**。

3. 结合论文原文的解析

论文原文写道:

“In order to keep the main component NMˇ/(qdivqℓ)≈NC2⋅qdiv/qℓN\check{M}/(q_{\text{div}}q_\ell) \approx N C^2 \cdot q_{\text{div}} / q_\ell small…” (原文这里可能有笔误,应该是 NMˇ2N\check{M}^2 而不是 NMˇN\check{M},根据 Lemma 4.2 的结论)

这里的逻辑链是:

  1. 前提: 假设 Mˇ≈C⋅qdiv\check{M} \approx C \cdot q_{\text{div}}。
  2. 公式: 主要误差源是 Mˇ2qdiv⋅qℓ\frac{\check{M}^2}{q_{\text{div}} \cdot q_\ell}。
  3. 代入: 分子变成了 (C⋅qdiv)2=C2⋅qdiv2(C \cdot q_{\text{div}})^2 = C^2 \cdot q_{\text{div}}^2。
  4. 约分: 分子的 qdiv2q_{\text{div}}^2 和分母的 qdivq_{\text{div}} 约掉一个。
  5. 结果: 剩下 C2⋅qdivC^2 \cdot q_{\text{div}} 在分子上。

总结

BOB: 4.2的结尾: 为什么Tensor步骤不需要NTT? Alt text

这是一个关于同态加密工程实现的经典问题。

简单直接的回答是:因为在高效的同态加密库(如 HEaaN)中,密文多项式默认就是以 NTT 形式(频域/点值表示)存储的。

当两个已经在 NTT 形式下的多项式做乘法时,只需要做逐点乘法(Hadamard Product),不需要再做 NTT 变换。

我为你详细拆解一下背后的逻辑:


1. 预备知识:多项式乘法的两种形态

假设我们要计算多项式乘法 C(x)=A(x)⋅B(x)C(x) = A(x) \cdot B(x)。


2. Tensor 步骤到底在干什么?

在同态加密的实际运行中,为了让每一步乘法都尽可能快,密文在绝大多数时间里都是保持在“形态 B”(NTT 形式)下的。

Tensor 步骤(计算交叉项): 我们要计算 (c0,c1)⊗(d0,d1)(c_0, c_1) \otimes (d_0, d_1),需要计算:

  1. c0⋅d0c_0 \cdot d_0
  2. c0⋅d1+c1⋅d0c_0 \cdot d_1 + c_1 \cdot d_0
  3. c1⋅d1c_1 \cdot d_1

因为 cic_i 和 did_i 本来就在 NTT 域里:


3. 为什么 Relin 和 RS 需要 NTT?

既然大家都在 NTT 域里待得好好的,为什么后两步需要做变换(涉及 NTT/INTT)呢?因为它们涉及到了**“不能在 NTT 域里直接做的操作”**。

A. Relin (Relinearization / Key Switching)

重线性化需要处理 d2d_2(即 c1⋅d1c_1 \cdot d_1)这一项。这通常涉及到 Key Switching。

B. RS (Rescaling)

重缩放的本质是计算 c(x)/Δc(x) / \Delta 并四舍五入。

总结

这就是为什么论文里说 Tensor 的开销比起后两者几乎可以忽略不计。

BOB: 向我详细说明一下: 4.3 Bounding the low parts的内容

这一节(4.3 Bounding the low parts)是整篇论文中非常关键的误差分析部分。

如果说前面的章节是在讲“怎么做”(算法步骤),那么这一节就是在讲“为什么这样做是安全的”以及“这个方法能跑多久”。

我为你详细拆解这一节的核心逻辑:


1. 核心问题:为什么我们需要关注“低位”的大小?

在 Mult2\text{Mult}^2 算法中,我们在 Tensor 步骤做了一个大胆的操作:扔掉了 ctˇ1⊗ctˇ2\check{ct}_1 \otimes \check{ct}_2(低位 ×\times 低位)。

所以,Section 4.3 的目的就是计算:做一次乘法后,新的低位部分到底会长大多少?


2. 分析工具:规范嵌入 (Canonical Embedding)

在进行误差分析时,作者特意强调了一个数学工具的选择:


3. Theorem 4.9 的推导逻辑

作者一步步追踪了 Mult2\text{Mult}^2 的三个步骤,看“低位”是怎么长大的:

第一步:Tensor 阶段

第二步:Relin 阶段

第三步:Rescale 阶段(关键)


4. 结论:它长得有多快?(1 bit per level)

让我们来分析上面那个核心公式: Mˇnew≈2M^Mˇqℓ\check{M}_{new} \approx \frac{2 \hat{M} \check{M}}{q_\ell}

为了看清它长了多少,我们需要代入一些近似关系:

  1. 高位与模数的关系: 高位 ct^\hat{ct} 是商。它的量级大约是 M^≈Scaleqdiv=Δqdiv\hat{M} \approx \frac{\text{Scale}}{q_{\text{div}}} = \frac{\Delta}{q_{\text{div}}}。
  2. 参数设计的巧合: 我们在参数选择时,特意让 Δ≈qdiv⋅qℓ\Delta \approx q_{\text{div}} \cdot q_\ell。 所以:M^≈qdiv⋅qℓqdiv=qℓ\hat{M} \approx \frac{q_{\text{div}} \cdot q_\ell}{q_{\text{div}}} = q_\ell。
  3. 最终化简: 把 M^≈qℓ\hat{M} \approx q_\ell 代入公式: Mˇnew≈2⋅qℓ⋅Mˇqℓ=2Mˇ\check{M}_{new} \approx \frac{2 \cdot q_\ell \cdot \check{M}}{q_\ell} = 2 \check{M}

震耳欲聋的结论: 做一次乘法,低位部分的数值大小(Bound)会变成原来的 2 倍。


5. 这个结论意味着什么?(指导意义)

这部分分析直接指导了我们在 6 EXPERIMENTS 中的策略:

  1. 不能无限乘下去:

    • 如果不处理,做 10 次乘法,低位就变大 2102^{10} 倍。
    • 做 50 次乘法,低位就变大 2502^{50} 倍。此时它已经和有效数据一样大了,再把它扔掉(Tensor),精度就全完了。
  2. 何时需要“洗澡”(Recombine):

    • 因为我们知道它每层长 1 bit。
    • 我们需要监控这个大小。当它大到一定程度(但在引起错误之前),我们必须执行 Recombine-then-Decompose。
    • 这就是为什么实验部分说:“recombination is interesting after 6 sequential applications”。
    • 因为长了 6 bits 还在可控范围内,这时候重置一下,把它压回初始状态,又可以继续跑了。

总结

4.3 节的核心就是论证了一个简单的规律:

虽然我们设计了复杂的机制来控制模数,但“低位垃圾”会以“每层增加 1 比特”的速度自然生长。

这个理论界限告诉我们:该方案是稳定的(线性增长而不是指数爆炸),但需要定期维护(Recombine)。

BOB: 在6.2 Increased homomorphic capacity中 我看到 1. 辅助模数的大小P似乎和scale一样大,这样的大小合适吗? 因为在重线性化中,密文要和重线性化密钥做乘法,这样的话,为了避免溢出,辅助模数的大小P不应该和层级模数Q_L一样大么? 2. 为什么图中说将log q从61改为57会使比较更为公平? 3. 使用的3个div素数可以是一样的吗? 4. Parameters with t = 1 and t = 2 showed -31.3 bits and -31.0 bits of precision, respectively.是什么意思? 5. 最后一段中说: Experiments show that this analysis is pessimistic and allow for better pairs (k, qdiv). 这是什么意思?意思是qdiv slightly smaller than √Δ是不可行的?? Alt text

这几个问题非常细致,涉及到 CKKS 参数选取的工程细节(Magic of Parameters)。我们逐个来剖析:

1. 辅助模数 PP 的大小问题

问题: 辅助模数的大小 PP 似乎和 scale 一样大,这样的大小合适吗?为了避免溢出,它不应该和层级模数 QLQ_L 一样大么?

回答: 在使用了 Gadget Decomposition(小工具分解)技术的前提下,这样是完全合适的。


2. 为什么将 log⁡q\log q 从 61 改为 57 会使比较“更为公平”?

回答:这是为了“强行拉齐精度”,以便在同等输出质量下比较计算深度。

逻辑链条如下:

  1. 前提: Mult2\text{Mult}^2 方案由于有“低位膨胀”问题,其噪声增长速度比标准 CKKS 快。
  2. 如果不改参数(都用 61 bits):
    • 标准 CKKS 的精度会很高(比如剩 40 bits 有效位)。
    • Mult2\text{Mult}^2 的精度会较低(比如只剩 30 bits 有效位)。
    • 这时候直接比较“谁跑得深”是不公平的,因为标准 CKKS “保留了实力”(精度过剩)。
  3. 降低标准 CKKS 的配置(61 →\to 57):
    • 降低模数比特数通常意味着降低 Scaling Factor Δ\Delta。
    • Δ\Delta 越小,信噪比(精度)通常越低。
    • 作者故意把标准 CKKS 的参数调弱,让它的最终精度也掉到和 Mult2\text{Mult}^2 差不多的水平(比如都是 30 bits)。
  4. 结果:
    • 即便降低了比特数(这其实对标准 CKKS 是有利的,因为模数小了,能塞进 QmaxQ_{max} 的层数就变多了,作者说这让它多跑了 1 层),标准 CKKS 依然只能跑 13 层。
    • 而 Mult2\text{Mult}^2 能跑 18 层。
    • 公平性: 在**“最终大家都剩这点精度”**的同等条件下,Mult2\text{Mult}^2 的计算深度依然完胜。

3. 使用的 3 个 div 素数可以是一样的吗?

回答:绝对不可以。


4. “-31.3 bits and -31.0 bits of precision” 是什么意思?

回答:这代表结果具有约 31 比特的有效精度。

这里的负号是相对误差的对数表示法。


5. 关于“Analysis is pessimistic”那段话

原文: “Experiments show that this analysis is pessimistic and allow for better pairs (k,qdiv)(k, q_{\text{div}}).”

回答:这句话的意思是“理论分析太保守了,实际上我们可以选更激进的参数(即 qdivq_{\text{div}} 可以比理论推导的更大)”。

BOB:对于1. 辅助模数 P 的大小问题,我还是没搞明白,能否详细解释?

这是一个非常关键的工程细节问题。你之前的困惑在于把**“不做分解的密钥切换”和“做了小工具分解(Gadget Decomposition)的密钥切换”**混淆了。

只要理解了**“噪声源是谁”**,你就能瞬间明白为什么 PP 可以变小。

我用最直观的数学逻辑给你推导一遍。


1. 密钥切换(Key Switching)的目标

假设我们有一个密文分量 cc(它对应着旧密钥 solds_{old}),它的系数可能非常大,最大可以达到模数 QLQ_L(比如 28002^{800})。 我们的目标是把它转换成对应新密钥 snews_{new} 的形式。

计算公式大致是: c⋅KSKc \cdot \text{KSK} 其中 KSK\text{KSK}(密钥切换公钥)包含了新密钥的信息。

2. 路径 A:不做分解(Naive Approach)—— 你的直觉

这是你脑海中的模型,也是 GHS 或者 Hybrid Key Switching 在不使用分解时的逻辑。

  1. 噪声源: KSK 本身是一个含噪声的密文:KSK≈P⋅snewsold+ekey\text{KSK} \approx \frac{P \cdot s_{new}}{s_{old}} + e_{key}。 当我们计算 c⋅KSKc \cdot \text{KSK} 时,噪声项变成了: Noise=c⋅ekey\text{Noise} = c \cdot e_{key}
  2. 噪声大小: 因为 cc 是模 QLQ_L 下的任意多项式,其系数大小 ∥c∥≈QL\|c\| \approx Q_L。 所以噪声大小为: Total Noise≈QL⋅ekey\text{Total Noise} \approx Q_L \cdot e_{key} (这是一个巨大的噪声,比如 2800⋅e2^{800} \cdot e)。
  3. 这就需要巨大的 PP: 为了消除这个噪声,我们需要把结果除以 PP(模数切换)。 Final Noise≈QL⋅ekeyP\text{Final Noise} \approx \frac{Q_L \cdot e_{key}}{P} 为了让最终噪声变小(比如变回 ee),必须要求 P≈QLP \approx Q_L。

结论: 如果不做分解,你的理解是对的,辅助模数必须和主模数一样大。


3. 路径 B:小工具分解(Gadget Decomposition)—— 本文的做法

这是论文实际采用的方法(RNS Decomposition)。

  1. 预处理(切碎): 我们在乘法之前,先把巨大的 cc 拆成 kk 个小块: c=c0+c1⋅B+c2⋅B2+…c = c_0 + c_1 \cdot B + c_2 \cdot B^2 + \dots

    • 这里 BB 是基数(在 RNS 中,BB 就是某一个素数 qiq_i 的大小)。
    • 关键点: 无论 QLQ_L 有多大,分解出来的每一个小块 cic_i,其大小严格受限于 BB(即 ∥ci∥<B\|c_i\| < B)。
  2. 对应的钥匙(KSK): 我们要准备一套钥匙 KSK0,KSK1,…\text{KSK}_0, \text{KSK}_1, \dots,分别对应每一块。

  3. 计算过程: 运算变成了“分块乘法再求和”: ∑(ci⋅KSKi)\sum (c_i \cdot \text{KSK}_i)

  4. 新的噪声源: 现在的总噪声是每一块噪声的累加: Total Noise=∑(ci⋅ekey)\text{Total Noise} = \sum (c_i \cdot e_{key})

    • 因为每一个 cic_i 都很小(≈B\approx B),
    • 所以总噪声大小为: Total Noise≈B⋅ekey⋅(块的数量)\text{Total Noise} \approx B \cdot e_{key} \cdot (\text{块的数量})
    • 注意!这里的乘数从巨大的 QLQ_L 变成了微小的 BB。
  5. P 需要多大? 同样执行除以 PP 的操作: Final Noise≈B⋅ekeyP\text{Final Noise} \approx \frac{B \cdot e_{key}}{P} 为了压住噪声,只要让 P≈BP \approx B 就足够了!


4. 映射到论文参数

总结

你之前的困惑在于认为 cc 是一个巨大的整体。 但在 Gadget Decomposition 技术下,巨大的 cc 被“切碎”成了无数个小 cic_i。

BOB: 是不是和这个很像? 我的理解是: 假设最高层级L上一共有L个模数,假设beta = (L+alpha-3)/alpha,即dnum = beta,则重现性化密钥swk_i有beta个,每个重线性化密钥swk_i代表alpha个素数模数。 对于层级Q_l, 步骤1.首先在P*Q_l上计算各个分量q_alpha_i上的值d_i 步骤2.将d_i与swk_i做内积 步骤3.使用rescale消去辅助模数P 我的疑问是, 对于步骤1,即使对于较低的层级,也要把所有q_alpha_i的分量d_i算出来吗?以及这个计算过程是CRT的逆过程吗? 对于步骤2,此时得到的是beta个结果,经过步骤三后得到的还是beta个结果,如何返回到RNS中l个素数表达的形式上? 以及在模数提升时,需要先从NTT域换回普通系数域? Alt text Alt text

这是一张非常经典的图片,描述的是 Hybrid Key Switching(混合密钥切换) 技术,通常被用于像 HEaaN 或 SEAL 这样的现代同态加密库中。

你的直觉非常敏锐,这确实和我们之前讨论的 Gadget Decomposition(小工具分解) 是同一个家族的东西,但它是针对 RNS 架构高度优化 的版本。

我们来详细拆解你的理解和疑问。


1. 是不是和 Gadget Decomposition 很像?

是的,它就是 Gadget Decomposition 的一种具体实现形式,叫做“RNS Digit Decomposition”。


2. 对步骤 1 的疑问:Lower levels & CRT 逆过程

疑问: 对于步骤1,即使对于较低的层级,也要把所有 qαiq_{\alpha_i} 的分量 did_i 算出来吗?

回答:不需要,通常是动态调整的。

疑问: 这个计算过程是 CRT 的逆过程吗?

回答:准确地说是“RNS 基扩展(RNS Basis Extension)”。

虽然它包含了 CRT 的思想,但在工程上流程如下:

  1. 提取: 你有密文 cc 在模 QℓQ_\ell 下的 RNS 表示。你取出了其中一组素数(对应 qαiq_{\alpha_i})的分量。
  2. 目标: 你需要把这个“小多项式”(模 qαiq_{\alpha_i})扩展到“大模数”(模 P⋅QℓP \cdot Q_\ell)。
    • 为什么?因为下一步要和 Key 做乘法,而 Key 是定义在 P⋅QℓP \cdot Q_\ell 上的。
  3. 操作流程:
    • INTT: 把这 α\alpha 个分量从 NTT 域转回系数域(Coefficient Form)。
    • CRT 重建(局部): 把它们组合成一个模 qαiq_{\alpha_i} 的整数多项式。
    • 取模映射: 把这个多项式的系数分别对 PP 和 QℓQ_\ell 中其他素数取模。
    • NTT: 再把所有分量转回 NTT 域。

所以,这比单纯的 CRT 逆过程要复杂,它是一个 “INTT →\to 提升 →\to NTT” 的完整基扩展过程。


3. 对步骤 2 & 3 的疑问:如何返回 RNS?

疑问: 对于步骤2,此时得到的是 β\beta 个结果,经过步骤三后得到的还是 β\beta 个结果,如何返回到 RNS 中 ll 个素数表达的形式上?

回答:你忽略了公式中的“内积(Inner Product)”含义。结果不是 β\beta 个,而是 1 个!

请看 Algorithm 3 的第 2 行: (a,b)←(⟨d,swk0⟩,⟨d,swk1⟩)(a, b) \leftarrow (\langle \mathbf{d}, \mathbf{swk}^0 \rangle, \langle \mathbf{d}, \mathbf{swk}^1 \rangle)

这里的尖括号 ⟨x,y⟩\langle \mathbf{x}, \mathbf{y} \rangle 代表 向量内积(Dot Product): Result=∑i=0β−1(di⋅swki)\text{Result} = \sum_{i=0}^{\beta-1} (d_i \cdot swk_i)

步骤 3(Modulus Down / Rescale by P):


4. 模数提升时,需要先从 NTT 域换回普通系数域?

回答:是的,绝对需要。

正如在回答步骤 1 时提到的,这是 RNS 运算的物理铁律:

这也就是为什么 Key Switching 比较慢的原因——它包含了一堆 NTT/INTT 变换。

总结你的理解

  1. 相似性: 是的,这就是 RNS 版本的 Gadget Decomposition。
  2. 步骤 1: 是基扩展(Basis Extension),需要 INTT →\to NTT。对于低层级,只需计算存活的 Digit。
  3. 步骤 2: 是内积求和,β\beta 个分量被加成了一个,所以不用担心怎么合并回去。
  4. NTT: 只要涉及模数变化(扩充或切换),必须回系数域(INTT)。

BOB: 有个疑问: 向量d和向量swk内积累加后,只得到 1 个 巨大的密文怎么存储? 我的理解是,在α个分量取模映射到P * Q_l后,就一直处于P * Q _l的RNS表示中了,也就是说,最后的密文结果也是RNS表示。

你的理解完全正确!满分!

所谓的“1 个巨大的密文”,在物理存储上绝不是存成一个几千比特的大整数(BigInt),而是一直保持在 P⋅QℓP \cdot Q_\ell 的 RNS 表示中。

我来把这个过程具象化一下,让你更清楚它在内存里长什么样。


1. 什么是“1 个巨大的密文”?

在数学上,这个密文是定义在模数 M=P⋅QℓM = P \cdot Q_\ell 下的。 假设 PP 有 kk 个素数,QℓQ_\ell 有 ℓ+1\ell+1 个素数。 总共有 k+ℓ+1k + \ell + 1 个小素数。

在物理存储(内存)中: 这个“巨大的密文”实际上就是一个包含 k+ℓ+1k + \ell + 1 个小多项式的列表(List/Vector)。

结构如下:

Huge_Ciphertext = {
  Poly_0 (mod q_0),   // 对应 Q_l 的第 0 个素数
  Poly_1 (mod q_1),
  ...
  Poly_l (mod q_l),   // 对应 Q_l 的最后一个素数
  Poly_p0 (mod p_0),  // 对应 P 的第 0 个素数
  ...
  Poly_pk (mod p_k)   // 对应 P 的最后一个素数
}

这就是你说的:“一直处于 P⋅QℓP \cdot Q_\ell 的 RNS 表示中”。


2. 内积 ∑(di⋅swki)\sum (d_i \cdot swk_i) 是怎么发生的?

这个内积运算,是在上述 RNS 结构的每一行上并行发生的。

假设我们有 β\beta 个分量(i=0…β−1i = 0 \dots \beta-1)。

运算逻辑: 计算机不会把它们拼成大数,而是对着每一行(每一个小素数 rr)做计算:

所有这些加法和乘法都是在**小素数域(64-bit)**内完成的。

3. 结果的形态

当循环结束后,你得到的 Result 依然是那个列表结构:

4. 下一步:Modulus Down (Rescale by P)

紧接着,算法会对这个结果执行 Algorithm 3 的第 3 步:

  1. 利用 PP 的那些分量,计算由于除以 PP 而产生的误差/调整值。
  2. 把这个调整值应用到 QℓQ_\ell 的那些分量上。
  3. 直接丢弃 PP 的那些分量(物理上释放内存)。

最终状态: 密文变回了只有 ℓ+1\ell+1 个分量的 RNS 列表(模 QℓQ_\ell)。

总结: 你的理解非常精准。全过程没有任何时刻会构建真正的“大整数”,数据流一直是: 小 RNS 分量 →\to 扩充为更多 RNS 分量 →\to RNS 分量对应相加 →\to 缩减 RNS 分量。

BOB:很好,我想我大致以及了解这篇论文《Homomorphic Multiple Precision Multiplication for CKKS and Reduced Modulus Consumption》了,我接下来要做的一件事情是,将现有的一个64bits素数系统的RNS-CKKS代码框架修改为32bits素数系统的代码。 修改的原因是,我目前实现了一个高效计算低于28bits素数的(I)NTT函数,我想将其接入扯个代码中。

这听起来是一个极其精彩且具有高度可行性的工程构想!

将 Mult2^2 (Double-CKKS) 算法与 < 28-bit 的高效 (I)NTT 结合,实际上是在利用这篇论文的核心优势来弥补小素数系统的天然劣势,同时最大化你的底层算子性能。

这简直就是天作之合。以下我为你分析为什么这是一个好主意,以及你在修改代码框架时需要注意的关键点。


1. 为什么这是一个“绝妙的组合”?

A. 弥补小素数的精度缺陷

B. 释放 32-bit 算子的性能潜力


2. 修改代码框架的实施指南

将 64-bit 框架改为 32-bit (<28-bit),你需要关注以下核心模块的重构:

Step 1: 基础数据类型与模乘算子 (The Foundation)

Step 2: NTT 算子接入 (The Core)

Step 3: Mult2^2 逻辑适配 (The Algorithm)

这是重头戏,你需要根据 28-bit 的特性调整算法参数:

  1. 素数选择:

    • 所有 RNS 素数 qiq_i(包括 qdivq_{\text{div}})都选在 27~28 bits 之间。
    • Total Scale: Δ≈255∼256\Delta \approx 2^{55} \sim 2^{56}。
  2. Mult2^2 流程调整:

    • Tensor: 逻辑不变,扔掉低位。
    • Relin2^2: 需要实现“乘以 qdivq_{\text{div}}”。
      • 注意:这里涉及将 qdivq_{\text{div}} 视为一个 28-bit 的常数。
    • RS2^2: Rescaling 的除数变成了 28-bit 的素数。
  3. Key Switching (Gadget Decomposition):

    • 在 64-bit 系统中,Gadget Base 通常是 2502^{50} 或 2602^{60}。
    • 在你的系统中,Gadget Base 自然变成了 2282^{28}。
    • 这实际上会让 Key Switching 的噪声变小(因为噪声与 Base 成正比),但可能需要更多的分解项(dnumdnum 变大)来覆盖同样的大模数范围。
    • 建议: 你的 dnumdnum 可能会翻倍(相比于 64-bit 系统),但单次乘法更快了。

Step 4: 误差控制与 Recombine (The Safety Net)

这是最需要小心的地方。


3. 可能遇到的坑 (Checklist)

  1. CRT 重建时的溢出:
    • 当你做 CRT(解密或 Modulus Switch)时,需要把多个 28-bit 的数拼成大整数。确保你的 BigInt 库能处理这些转换。
  2. Scaling Factor 的匹配:
    • 确保 qdivq_{\text{div}} 和 qℓq_\ell 的乘积是你想要的 Δ\Delta。如果 28+28=5628+28=56 bits 对你来说太大了(比如你只想用 40 bits 精度),你可以选更小的素数(比如 20 bits),或者让 qdivq_{\text{div}} 选小一点。
  3. 素数生成器:
    • 原框架的素数生成器可能默认生成 50+ bits 的素数,记得把它改成生成 <28 bits 的素数,且要支持足够多的数量(因为现在每层只需要 1 个素数,但总层数可能较深)。

总结

你的计划非常靠谱。

这就像是把一台重型卡车(64-bit CKKS)的引擎拆了,换成了一组虽然排量小但转速极高的赛车引擎(32-bit RNS),然后通过双涡轮增压(Mult2^2)保证了动力输出(精度)不减。

祝你代码修改顺利!如果遇到具体的溢出问题或者参数调试问题,欢迎随时来问。


全文总结

通过对论文《Homomorphic Multiple Precision Multiplication for CKKS and Reduced Modulus Consumption》的深入学习,我们系统地理解了以下核心内容:

核心贡献

  1. Mult² 算法:通过将密文分解为高位和低位两部分,实现了模数消耗减半的目标
  2. 弱形式欧几里得除法:利用代数恒等式确保误差自动抵消,无需手动控制
  3. 对表示法(Pair Representation):维护密文对 (cthigh,ctlow)(ct_{\text{high}}, ct_{\text{low}}) 来实现高精度计算

关键技术点

实际应用价值

学习收获

通过这次深度对话,我们不仅理解了论文的数学原理,更重要的是掌握了:

这篇论文为 CKKS 同态加密的实用化提供了重要的优化方向,是理解现代全同态加密系统不可或缺的重要文献。


文章完成于 2025年12月23日


编辑此页
Share this post on:

Previous Post
关于GPU实现中的模%和Montgomery约简间的差距
Next Post
你好,互联网!这是我的第一篇博文