跳到正文
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 (除数,且 b0b \neq 0),存在唯一的整数 qq (商) 和 rr (余数),满足: a=bq+ra = b \cdot q + r 其中 0r<b0 \le r < |b|

在论文中的应用

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

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

这就像把数字 1234512345 按照 100100 分解:12345=100×123+4512345 = 100 \times 123 + 45123123 是高位,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=2kct^+ctˇct = 2^k \cdot \hat{ct} + \check{ct}
  2. 解密即线性运算: 解密函数 Dec()\text{Dec}(\cdot) 是线性的。所以: m=Dec(ct)=2kDec(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 的,所以你实际上多算了 2ke^2^k \cdot \hat{e}。为了让总数保持不变(还是原来的 mm),你的“余数”(低位)就必须相应地少算 2ke^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 中密文却是 2kQ2^k \cdot Q?对于 CKKS 方案,明文和密文多项式系数有什么大小限制吗?
  5. 1.1节最后的图中在说些什么?

回答

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

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

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

Decompose 的做法: 它把一个大数 mm 拆成两半:高位 m1m_1 和 低位 m0m_0,即 m=m12k+m0m = m_1 \cdot 2^k + m_0。 这里 m1m_1m0m_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)),即: m2kDec(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ˇ=ct2kct^\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(ct2kct^)D(\check{ct}) = D(ct - 2^k \cdot \hat{ct}) D(ctˇ)=D(ct)2kD(ct^)D(\check{ct}) = D(ct) - 2^k \cdot D(\hat{ct})

移项得到: D(ct)=2kD(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} 的定义就是 ct2kct^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}),其中 ct2kct^+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(2kct^))+(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. 放大高位(2kct^2^k \cdot \hat{ct}:作者没有直接处理 ct^\hat{ct},而是先把它乘上 2k2^k。这样它的量级就和原始密文一样了。

  2. 在大量级上做 Relin:计算 Relin(2kct^)\text{Relin}(2^k \cdot \hat{ct})。此时引入的噪声 ee 是加在 2kct^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(2kct^+ctˇ)2kRS(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)2kNewHigh\text{NewLow} = \text{RS}(\text{Total}) - 2^k \cdot \text{NewHigh} (注:公式里的 2kct^+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} 时,因为减去了 2kNewHigh2^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}^2RS2\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(2kct^))部分 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=2kct^+ctˇct = 2^k \cdot \hat{ct} + \check{ct} 根据带余除法的定义,低位 ctˇ\check{ct} 是余数,所以它的系数绝对值很小,被严格限制在 2k/2\le 2^k/2

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

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

Mult2\text{Mult}^2 试图计算 A×BA \times B。根据 Tensor2\text{Tensor}^2 的定义,新的低位部分(在逻辑上)大致对应于交叉项: NewLowA^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ˇ1ctˇ2\check{ct}_1 \otimes \check{ct}_2 这一项吗?

    • 起初:因为 ctˇ\check{ct} 很小,所以”小 ×\times 小”微不足道,丢了也就丢了,误差很小。
    • 膨胀后:如果 ctˇ\check{ct} 变得很大,那么 ctˇ1ctˇ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=2kct^+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. qq_{\ell}:是我们在做 Rescaling 时扔掉的模数(也就是我们要消耗的成本)。

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

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

我们现在的目标是 logΔ=100\log \Delta = 100。这意味着 logqdiv+logq100\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),而模数消耗是矩形的较长边(因为 qq_{\ell} 必须既补足精度,又要盖过 qdivq_{\text{div}},取两者的最大值)。

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


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

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

回答

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

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

结论先行

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

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

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

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

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

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

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

步骤 C:Rescaling 的除法

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

步骤 D:约束条件

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

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


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

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

假设目标总缩放因子 Δ=qdivq\Delta = q_{\text{div}} \cdot q_{\ell},我们需要 logΔ=100\log \Delta = 100

方案 1:均分(50 / 50)

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

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

总结

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

“模数 qq_{\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Δ)=(AB)Δ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(显式除法) 这是算法最后的重缩放步骤,会再除以模数 qq_{\ell}。 此时结果的缩放规模变成了: Δ2qdivq\frac{\Delta^2}{q_{\text{div}} \cdot q_{\ell}}

最终效果: 因为我们预先设计好了 qdivqΔ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. 我们的分解是 ctqdivct^ct \approx q_{\text{div}} \cdot \hat{ct}(忽略低位)。
  2. 做乘法时,我们计算的是 Tensor2ct^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=ct1ct2qdiv2\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=qdivHigh+LowResult = q_{\text{div}} \cdot \text{High} + \text{Low},这个结果会被乘回一个 qdivq_{\text{div}}
  5. 综合算一下: (ct1ct2qdiv2)×qdiv=ct1ct2qdiv\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 这一步只需要处理剩余的那一半缩放(qq_{\ell}),所以它消耗的模数比标准做法小得多。而另一半缩放(qdivq_{\text{div}})被算法的结构设计给”白嫖”了。这就是这篇论文降低模数消耗的秘密武器。


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

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

回答

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

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

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

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

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

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

后果推演:

  1. 分解阶段(Decompose): 你把密文分解为 ct=2100ct^+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): 这是原本用来消除噪声的步骤。 公式是:除以 qq_{\ell}。 但因为你太贪心,设了 q=1q_{\ell} = 1(为了不消耗模数)。

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

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


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

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

数学上的硬性约束(来自 Theorem 4.8): 为了让最终误差 1\le 1(或者可控),必须满足: 产生的噪声消除的能力qdivq1\frac{\text{产生的噪声}}{\text{消除的能力}} \approx \frac{q_{\text{div}}}{q_{\ell}} \le 1 也就是说: qqdivq_{\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} 的计算公式通常很简单,就是当前模数的一半(减去消息本身的大小):

BDecQ2mB_{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+13k+1 \ge 3),模 MM 的乘法群不是循环群(没有单一生成元),而是同构于两个循环群的直积: (Z/2k+1Z)×C2×C2k1(\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})。
    • C2k1C_{2^{k-1}} (阶为 N/2N/2 的子群): 需要一个元素,它在模 2k+12^{k+1} 下的阶是 2k12^{k-1}5 就是满足这个条件的最小整数。
  4. 为什么是 5?验证一下:

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

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

推荐的阅读资料与博客

关于这个问题,通常在介绍 HElibSEALRing-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,则实际大小是 xQx-Q”。用对称区间可以省去这个麻烦,且符合误差分布(通常是高斯分布,均值为 0)的直觉。


2. 关于 Gadget Decomposition

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

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

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

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

Gadget Decomposition 的魔法: 它把大数 cc 拆成了一堆小数 cic_ic=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 这一位的加密。

计算变成了: (ciKSi)\sum (c_i \cdot KS_i)

为什么省模数?

  1. 噪声变小了: 现在的噪声不再是 QeQ \cdot e,而是 Be\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_iqi\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(即 mm+Q0Im \to m + Q_0 \cdot I)。
  2. CtS(变换): 为了方便计算,把这些混杂了误差的系数搬运到 SIMD 的里。
  3. EvalMod(清洗): 在槽里跑一个高次多项式,模拟“取模”操作,把 Q0IQ_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=0qiQ_\ell = \prod_{i=0}^{\ell} q_i.”

这就意味着 QQ_\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,,qq_1, \dots, q_\ell (Level Primes): 这些是用来支持 \ell 层乘法的“燃料”。每做一次 Rescaling,就会消耗掉最上面的一个素数。
  3. 在 Definition 3.3 中的语境:

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

总结: QQ_\ell 包含 1 个基础素数 (q0q_0) 加上 \ell 个乘法素数 (q1qq_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 的模数链 QQ_\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}} 对应的分量,然后在其他分量上乘以 qdiv1q_{\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 YXY=(12100+34)×(56100+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) 在模 QQ_\ell 下的 RNS 表示。

为了看到“消息”,你需要做 CRT 重建(CRT Composition): 利用中国剩余定理公式,把这些小多项式拼回去: M(X)=i=0(m(i)(X)Q^iQ^i1)(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) 的系数从模 QQ_\ell 域([0,Q)[0, Q_\ell))映射到有符号整数域([Q/2,Q/2)[-Q_\ell/2, Q_\ell/2))。
  2. 除以缩放因子: 每个系数除以 Δ\DeltaResult(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=qdivNewHigh+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 乘法” 放在一起比较。

我们把上面的“标准乘法展开式”变形一下: XY=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 的核心逻辑等式: XY=qdiv(Tensor2的结果)+(扔掉的低位乘积)X \cdot Y = q_{\text{div}} \cdot (\text{Tensor}^2 \text{的结果}) + (\text{扔掉的低位乘积})

对应论文中的公式: (ct1ct2)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 的结果,大约等于 (XY)/qdiv(X \cdot Y) / q_{\text{div}}。这就解释了为什么我说它自带“隐式除法”功能。
  2. 误差来源: 剩下的误差项就是 XˇYˇ\check{X}\check{Y}(即论文中的 (ctˇ1sk)(ctˇ2sk)(\check{ct}_1 \cdot sk)(\check{ct}_2 \cdot sk))。

4. 误差界限(Bound)解析

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

论文给出的界限是: ErrorNMˇ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 是在用数学语言说:

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

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

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

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

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


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

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

然而,CKKS 的密文是存储在 QQ_\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 有多大。 根据分解定义: mqdivm^+mˇm \approx q_{\text{div}} \cdot \hat{m} + \check{m}

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

第二步:估算乘积的大小

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

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

将第一步的界限代入: m1m2N(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}) m1m2N(qdivM^+Mˇ)2\|m_1 \cdot m_2\|_\infty \le N (q_{\text{div}}\hat{M} + \check{M})^2

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

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

乘积的大小<Q2\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. 物理意义解读

这个公式实际上是在告诉我们:当前的模数 QQ_\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 页):

(ct1ct2)(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=(ct1ct2)(1,s,s2)\text{LHS} = (ct_1 \otimes ct_2) \cdot (1, s, s^2)

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

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

这里的逻辑链条

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

  2. 我们需要的是“真相等”: 为了证明误差分析是正确的,我们需要这个等式在整数环 RR 上直接成立(即 A=BA=B,而不仅仅是 ABA \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(也就是 m1m2m_1 \cdot m_2)的最大可能值都比模数的一半要小。”

    • 既然 LHS <Q/2< Q_\ell/2(没有溢出)。
    • 且 RHS 在数学上等于 LHS。
    • 那么 RHS <Q/2< Q_\ell/2(也没有溢出)。
    • 结论: 既然两边都没溢出,且模 QQ_\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}')

即公式:DCPqdivRelinRCBqdiv(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(qdivct^))\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} 处于模数 QQ_\ell 下,RNS 基为 {q0,q1,,q}\{q_0, q_1, \dots, q_\ell\}。 我们的目标是获得一个处于模数 QqdivQ_\ell \cdot q_{\text{div}} 下的新密文。

具体实现步骤如下:

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

对于当前已经在 QQ_\ell 中的每一个 RNS 模数 qiq_i (i=0i=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。
    • 状态: 密文现在拥有了 QqdivQ_\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 的分量),然后把其他分量乘以 qdiv1q_{\text{div}}^{-1}
    • 状态: 密文回到了 QQ_\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}),另一半(qdiv250q_{\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 168\approx 8
最大总模数 (logQmax\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. 模数变小(logQ\log Q):
    • 标准密文的模数 Q1000Q \approx 1000 bits。
    • Mult2\text{Mult}^2 密文的模数 Q680Q \approx 680 bits。
    • 单体变小了约 30%。
  2. 环维数减半(NN): 这是最关键的!
    • 因为模数变小了,根据 LWE 安全性估算,我们可以把环维数 NN2162^{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. qq_{\ell} (约 50 bits):

    • 这是**“显式 Scaling”**。
    • RS2\text{RS}^2 步骤中,通过物理除法,数据被缩小了 qq_{\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)应该是 Cqdiv\approx C \cdot q_{\text{div}},而不是 C2qdivC^2 \cdot q_{\text{div}}

那个出现的 C2qdivC^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ˇCqdiv\check{M} \approx C \cdot q_{\text{div}} (其中 CC 是一个接近 0.50.5 或稍大一点的常数,考虑到噪声增长)

2. 为什么出现了 C2qdivC^2 \cdot q_{\text{div}}

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

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

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

Error(Cqdiv)2qdiv\text{Error} \approx \frac{(C \cdot q_{\text{div}})^2}{q_{\text{div}}} =C2qdiv2qdiv= \frac{C^2 \cdot q_{\text{div}}^2}{q_{\text{div}}} =C2qdiv= \mathbf{C^2 \cdot q_{\text{div}}}

真相大白: 文中提到的 C2qdivC^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)NC2qdiv/qN\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ˇCqdiv\check{M} \approx C \cdot q_{\text{div}}
  2. 公式: 主要误差源是 Mˇ2qdivq\frac{\check{M}^2}{q_{\text{div}} \cdot q_\ell}
  3. 代入: 分子变成了 (Cqdiv)2=C2qdiv2(C \cdot q_{\text{div}})^2 = C^2 \cdot q_{\text{div}}^2
  4. 约分: 分子的 qdiv2q_{\text{div}}^2 和分母的 qdivq_{\text{div}} 约掉一个。
  5. 结果: 剩下 C2qdivC^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. c0d0c_0 \cdot d_0
  2. c0d1+c1d0c_0 \cdot d_1 + c_1 \cdot d_0
  3. c1d1c_1 \cdot d_1

因为 cic_idid_i 本来就在 NTT 域里


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

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

A. Relin (Relinearization / Key Switching)

重线性化需要处理 d2d_2(即 c1d1c_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ˇ1ctˇ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ˇnew2M^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. 参数设计的巧合: 我们在参数选择时,特意让 Δqdivq\Delta \approx q_{\text{div}} \cdot q_\ell。 所以:M^qdivqqdiv=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ˇnew2qMˇ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. 为什么将 logq\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} 的形式。

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

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

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

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

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


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

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

  1. 预处理(切碎): 我们在乘法之前,先把巨大的 cc 拆成 kk 个小块: c=c0+c1B+c2B2+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. 计算过程: 运算变成了“分块乘法再求和”: (ciKSKi)\sum (c_i \cdot \text{KSK}_i)

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

    • 因为每一个 cic_i 都很小(B\approx B),
    • 所以总噪声大小为: Total NoiseBekey(块的数量)\text{Total Noise} \approx B \cdot e_{key} \cdot (\text{块的数量})
    • 注意!这里的乘数从巨大的 QLQ_L 变成了微小的 BB
  5. P 需要多大? 同样执行除以 PP 的操作: Final NoiseBekeyP\text{Final Noise} \approx \frac{B \cdot e_{key}}{P} 为了压住噪声,只要让 PBP \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(混合密钥切换) 技术,通常被用于像 HEaaNSEAL 这样的现代同态加密库中。

你的直觉非常敏锐,这确实和我们之前讨论的 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 在模 QQ_\ell 下的 RNS 表示。你取出了其中一组素数(对应 qαiq_{\alpha_i})的分量。
  2. 目标: 你需要把这个“小多项式”(模 qαiq_{\alpha_i})扩展到“大模数”(模 PQP \cdot Q_\ell)。
    • 为什么?因为下一步要和 Key 做乘法,而 Key 是定义在 PQP \cdot Q_\ell 上的。
  3. 操作流程:
    • INTT: 把这 α\alpha 个分量从 NTT 域转回系数域(Coefficient Form)。
    • CRT 重建(局部): 把它们组合成一个模 qαiq_{\alpha_i} 的整数多项式。
    • 取模映射: 把这个多项式的系数分别对 PPQQ_\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(diswki)\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),而是一直保持在 PQP \cdot Q_\ell 的 RNS 表示中

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


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

在数学上,这个密文是定义在模数 M=PQM = P \cdot Q_\ell 下的。 假设 PPkk 个素数,QQ_\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 的最后一个素数
}

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


2. 内积 (diswki)\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. 把这个调整值应用到 QQ_\ell 的那些分量上。
  3. 直接丢弃 PP 的那些分量(物理上释放内存)。

最终状态: 密文变回了只有 +1\ell+1 个分量的 RNS 列表(模 QQ_\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: Δ255256\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}}qq_\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
你好,互联网!这是我的第一篇博文