Seal库官方示例(五):ckks_basics.cpp解析

news2024/12/26 11:39:51

这个代码计算的是 π × x 3 + 0.4 × x + 1 \pi \times x^3+0.4 \times x +1 π×x3+0.4×x+1

代码解析

方案选择

首先照例是方案选择

EncryptionParameters parms(scheme_type::ckks);

参数设置

CKKS方案中使用rescale方法来控制膨胀的密文规模和噪声,这个和modulus switching有点类似,如果想要获取良好的规模控制,那么对于系数模的选取就很重要。
和论文CKKS方案里的一样,将模数链上的模数 P i P_i Pi设定为和缩放因子接近的大小,如此就可以将规模维持在接近原来的样子,这样一来,能计算的乘法深度就和模数链的长度有关,做一次乘法预算就会消耗一个模数,除此之外,需要将模数链上的第一个模数设定要更大一些来保障明文小数点之前值的精确度,也就是说最后一次rescale时,将用这个模数来保证精确度。因此,官方给出的选取策略是(首尾选最大,中间选一样)

  1. 选取60-bit的素数作为模数链的第一个模数,这能在解密的时候保证最大的精确度
  2. 选择另外一个60-bit的素数作为模数链的最后一个模数,它将作为special prime,前面说过它必须和模数链里最大的一样大。
  3. 其他模数选择基本上接近的

接下来参数设置,根据阶数得出模数最大为218,这里选取的200。

size_t poly_modulus_degree = 8192;
parms.set_poly_modulus_degree(poly_modulus_degree);
parms.set_coeff_modulus(CoeffModulus::Create(poly_modulus_degree, { 60, 40, 40, 60 }));

缩放因子设置

选择初始的缩放因子scale,这里选择的是 2 40 2^{40} 240,和上面的模数接近,最后一次rescale时会获得大致小数点前60-40=20bit的精确度

double scale = pow(2.0, 40);

SEALContext context(parms);
print_parameters(context);
cout << endl;

image.png

密钥生成与加解密器实例化

然后是常规的密钥生成、加密器解密器实例化

KeyGenerator keygen(context);
auto secret_key = keygen.secret_key();
PublicKey public_key;
keygen.create_public_key(public_key);
RelinKeys relin_keys;
keygen.create_relin_keys(relin_keys);
GaloisKeys gal_keys;
keygen.create_galois_keys(gal_keys);
Encryptor encryptor(context, public_key);
Evaluator evaluator(context);
Decryptor decryptor(context, secret_key);

插槽设置

CKKSEncoder encoder(context);
size_t slot_count = encoder.slot_count();
cout << "Number of slots: " << slot_count << endl;

明文输入

明文输入

vector<double> input;
input.reserve(slot_count);
double curr_point = 0;
double step_size = 1.0 / (static_cast<double>(slot_count) - 1);
for (size_t i = 0; i < slot_count; i++)
{
    input.push_back(curr_point);
    curr_point += step_size;
}
cout << "Input vector: " << endl;
print_vector(input, 3, 7);

cout << "Evaluating polynomial PI*x^3 + 0.4x + 1 ..." << endl;

编码和加密

编码并加密,编码将把浮点值编码到向量的每个插槽

Plaintext plain_coeff3, plain_coeff1, plain_coeff0;
encoder.encode(3.14159265, scale, plain_coeff3);
encoder.encode(0.4, scale, plain_coeff1);
encoder.encode(1.0, scale, plain_coeff0);

Plaintext x_plain;
print_line(__LINE__);
cout << "Encode input vectors." << endl;
encoder.encode(input, scale, x_plain);
Ciphertext x1_encrypted;
encryptor.encrypt(x_plain, x1_encrypted);

image.png

同态计算

这里计算的顺序很重要,乘法越多产生的噪声越多。
先计算 x 2 x^2 x2,并看看rescale前后的scale的变化

Ciphertext x3_encrypted;
print_line(__LINE__);
cout << "Compute x^2 and relinearize:" << endl;
evaluator.square(x1_encrypted, x3_encrypted);
evaluator.relinearize_inplace(x3_encrypted, relin_keys);
cout << "    + Scale of x^2 before rescale: " << log2(x3_encrypted.scale()) << " bits" << endl;
print_line(__LINE__);
cout << "Rescale x^2." << endl;
evaluator.rescale_to_next_inplace(x3_encrypted);
cout << "    + Scale of x^2 after rescale: " << log2(x3_encrypted.scale()) << " bits" << endl;

可以看到是还原到了以前的规模
image.png
注意,现在 x 2 x^2 x2的密文和x的密文不是一个level的了(因为rescale了一下,它到了下一个level),同一个level应当具有相同的加密参数(每一层所使用的参数都是不一样的)和相同的scale,否则不能进行加法或乘法运算。当然可以选择用modulus switching将其切换为下一个level,但是考虑到要计算的是三次幂,所以选择计算 π × x \pi \times x π×x
接下来计算 π × x \pi \times x π×x并rescale一下。

print_line(__LINE__);
cout << "Compute and rescale PI*x." << endl;
Ciphertext x1_encrypted_coeff3;
evaluator.multiply_plain(x1_encrypted, plain_coeff3, x1_encrypted_coeff3);
cout << "    + Scale of PI*x before rescale: " << log2(x1_encrypted_coeff3.scale()) << " bits" << endl;
evaluator.rescale_to_next_inplace(x1_encrypted_coeff3);
cout << "    + Scale of PI*x after rescale: " << log2(x1_encrypted_coeff3.scale()) << " bits" << endl;

image.png
这样一操作,现在它们都在同一个level上了(具有相同scale,相同加密参数),就能进行合并乘法了。
计算 ( π × x ) × x 2 (\pi \times x)\times x^2 (π×x)×x2

print_line(__LINE__);
cout << "Compute, relinearize, and rescale (PI*x)*x^2." << endl;
evaluator.multiply_inplace(x3_encrypted, x1_encrypted_coeff3);
evaluator.relinearize_inplace(x3_encrypted, relin_keys);
cout << "    + Scale of PI*x^3 before rescale: " << log2(x3_encrypted.scale()) << " bits" << endl;
evaluator.rescale_to_next_inplace(x3_encrypted);
cout << "    + Scale of PI*x^3 after rescale: " << log2(x3_encrypted.scale()) << " bits" << endl;

image.png
再经历又一次的计算和rescale后,密文到了最后一个level。(一共4个模数,即4个level,密钥生成在第一个,密文诞生第二个,平方第三个,三次幂第四个,下面有表格说明)。
然后计算 0.4 × x 0.4 \times x 0.4×x

print_line(__LINE__);
cout << "Compute and rescale 0.4*x." << endl;
evaluator.multiply_plain_inplace(x1_encrypted, plain_coeff1);
cout << "    + Scale of 0.4*x before rescale: " << log2(x1_encrypted.scale()) << " bits" << endl;
evaluator.rescale_to_next_inplace(x1_encrypted);
cout << "    + Scale of 0.4*x after rescale: " << log2(x1_encrypted.scale()) << " bits" << endl;

image.png
现在想把三个项合起来,但是三个项的所用的加密参数、scale是不同的,也就是level不一样,那么接下来要做的就是切换到同一个level上
获取密文项的level。

cout << endl;
print_line(__LINE__);
cout << "Parameters used by all three terms are different." << endl;
cout << "    + Modulus chain index for x3_encrypted: "
     << context.get_context_data(x3_encrypted.parms_id())->chain_index() << endl;
cout << "    + Modulus chain index for x1_encrypted: "
     << context.get_context_data(x1_encrypted.parms_id())->chain_index() << endl;
cout << "    + Modulus chain index for plain_coeff0: "
     << context.get_context_data(plain_coeff0.parms_id())->chain_index() << endl;
cout << endl;

可以看到level是不一样的
image.png

获取密文项的scale

print_line(__LINE__);
cout << "The exact scales of all three terms are different:" << endl;
ios old_fmt(nullptr);
old_fmt.copyfmt(cout);
cout << fixed << setprecision(10);
cout << "    + Exact scale in PI*x^3: " << x3_encrypted.scale() << endl;
cout << "    + Exact scale in  0.4*x: " << x1_encrypted.scale() << endl;
cout << "    + Exact scale in      1: " << plain_coeff0.scale() << endl;
cout << endl;
cout.copyfmt(old_fmt);

可以看到,尽管此时三项的scale都是接近40,但是它的确切值是不同,而且加密参数不同,所以是不能相加的。
image.png

现在来集中回顾一下密文计算中项的level变化,假定模数链上的模数为 P 0 , P 1 , P 2 , P 3 P_0,P_1,P_2,P_3 P0,P1,P2,P3

scale, levelafter rescale, level
x 2 x^2 x2 80 , 2 80,2 80,2 80 / P 2 , 1 80/P_2,1 80/P2,1
π × x \pi \times x π×x 80 , 2 80,2 80,2 80 / P 2 , 1 80/P_2,1 80/P2,1
π × x 3 \pi \times x^3 π×x3 ( 80 / P 2 ) 2 (80/P_2)^2 (80/P2)2,1 ( 80 / P 2 ) 2 / P 1 (80/P_2)^2/P_1 (80/P2)2/P1,0
0.4 × x 0.4 \times x 0.4×x 80 , 2 80,2 80,2 80 / P 2 , 1 80/P_2,1 80/P2,1
1 1 1 40 , 2 40,2 40,2

接下来要做的是,将scale和加密参数都变为一致。
改变scale,有两种方式,

  • 一种是直接强行将三项的scale设置为一样的(最简单的)
  • 另一种是将明文1编码到和 0.4 × x 0.4 \times x 0.4×x一样的level,然后相乘,然后rescale

本例用的是第一种方法
设置scale

print_line(__LINE__);
cout << "Normalize scales to 2^40." << endl;
x3_encrypted.scale() = pow(2.0, 40);
x1_encrypted.scale() = pow(2.0, 40);

有关加密参数的切换,直接使用modulus switching,不用rescale即可

print_line(__LINE__);
cout << "Normalize encryption parameters to the lowest level." << endl;
parms_id_type last_parms_id = x3_encrypted.parms_id();
evaluator.mod_switch_to_inplace(x1_encrypted, last_parms_id);
evaluator.mod_switch_to_inplace(plain_coeff0, last_parms_id);

现在把三个项相加即可

print_line(__LINE__);
cout << "Compute PI*x^3 + 0.4*x + 1." << endl;
Ciphertext encrypted_result;
evaluator.add(x3_encrypted, x1_encrypted, encrypted_result);
evaluator.add_plain_inplace(encrypted_result, plain_coeff0);

解码解密并输出结果
这里先是直接手动计算了一下值,然后再将密文解密,解码

Plaintext plain_result;
print_line(__LINE__);
cout << "Decrypt and decode PI*x^3 + 0.4x + 1." << endl;
cout << "    + Expected result:" << endl;
vector<double> true_result;
for (size_t i = 0; i < input.size(); i++)
{
    double x = input[i];
        true_result.push_back((3.14159265 * x * x + 0.4) * x + 1);
}
print_vector(true_result, 3, 7);

decryptor.decrypt(encrypted_result, plain_result);
vector<double> result;
encoder.decode(plain_result, result);
cout << "    + Computed result ...... Correct." << endl;
print_vector(result, 3, 7);

可以看到是有一定的误差的,CKKS是近似计算。
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/55833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[激光原理与应用-28]:《激光原理与技术》-14- 激光产生技术 - 激光的主要参数与指标

目录 1、 激光器的门限电流与功率输出 2、激光器的调制增益 3、功率/能量密度 6、额定功耗 7、转换效率 8、光斑大小 9、线宽 10、激光器的谱线宽度。 11、激光器的相对强度噪声RIN。 12、激光器的线性范围。 13、带内平坦度 14、激光器的温度特性 15、激光器的交…

基于PHP+MySQL信息技术学习网站设计与实现

智多在线网络学习平台为学习各种技术查看资料的用户提供一个准确、最新的技术与相关文档&#xff0c;浏览目前流行教学的新闻&#xff0c;提出技术上遇到的难点及问题&#xff0c;帮助其他用户回答所提出的问题&#xff0c;上传想要分享的资源&#xff0c;下载要获取的相关技术…

Spirng MVC——获取参数详解

文章目录1. 什么是 Spirng MVC1.1 MVC 定义1.2 MVC 和 Spring MVC 的关系2. 创建Spring MVC 项目3. Spring MVC 学习目标3.1 实现用户和程序的映射方法1&#xff1a;RequestMapping("/xxx")方法2&#xff1a;使用 POSTMapping("/xxx")方法3&#xff1a;使用…

Kali生成windows木马程序

目录 一、生成windows执行木马程序 二、进入msfconsole进行监听目标上线 三、目标运行木马和后渗透 四、问题 Meterpreter session 2 closed. Reason: Died 一、生成windows执行木马程序 -p windows/x64/meterpreter/reverse_tcp //载入64位payload攻击载荷&#xff0c…

RabbitMQ-惰性队列

文章目录1 消息堆积问题2 惰性队列2.1 基于命令行设置lazy-queue2.2 基于Bean声明LazyQueue(推荐)2.3 基于RabbitListener声明LazyQueue3 总结3.1 消息堆积问题的解决方案&#xff1f;3.2 惰性队列的优点有哪些&#xff1f;3.3 惰性队列的缺点有哪些&#xff1f;1 消息堆积问题…

BSA-maltose 牛血清白蛋白修饰麦芽糖 BSA-麦芽糖

产品名称&#xff1a;牛血清白蛋白修饰麦芽糖 英文名称&#xff1a;BSA-maltose 用途&#xff1a;科研 状态&#xff1a;固体/粉末/溶液 产品规格&#xff1a;1g/5g/10g 保存&#xff1a;冷藏 储藏条件&#xff1a;-20℃ 储存时间&#xff1a;1年 牛血清中的简单蛋白&#xff…

JAVA数据类型与变量

JAVA初阶 背景了解 Java语言之父—>高斯林。现代计算机之父—>冯诺依曼。 Java当中的main方法。 .java ->编译javac xxx.java -> xxx.class[字节码文件&#xff1a;二进制文件]。java命令 运行java程序 public class HelloWorld {//m main psvm 出现之后回车即可…

Qt第二十七章:QWidget、QMainWindow自定义标题栏并自由移动缩放

前提&#xff1a;UI必須采用自适应布局。 自定义组件【直接CV】custom_components.py from PySide6 import QtGui, QtWidgets, QtCore from PySide6.QtCore import Qt, QSize, QRect from PySide6.QtWidgets import QPushButton, QLabel, QWidgetclass QCustomTitleBar:def _…

基于SpringBoot vue的茶叶商城平台源码和论文含支付宝沙箱支付

此项目是前后端分离的 后台项目:shop 前端项目:Vue-shop 后端项目启动步骤: 1.先把sql导入数据库 2.把后台项目导入编辑器 3.修改数据库配置 4.启动项目 前端项目启动步骤: 1.打开Vue-shop目录,在这个文件夹里面加入cmd目录窗口 2.运行启动vue项目目录(需先安装nodejs软件)…

四、伊森商城 前端基础-Vue 双向绑定事件处理安装插件 p22

1、双向绑定 双向绑定&#xff1a; 效果&#xff1a;我们修改表单项&#xff0c;num 会发生变化。我们修改 num&#xff0c;表单项也会发生变化。为了实 时观察到这个变化&#xff0c;我们将 num 输出到页面。 我们不需要关注他们为什么会建立起来关联&#xff0c;以及页面如何…

Java面向对象三大特性:继承、封装、多态

java封装、继承、多态笔记 1.包 1.包的命名规则 &#xff08;1&#xff09;只能包含数字、字母、下划线、小圆点. &#xff08;2&#xff09;不能用数字开头&#xff0c; &#xff08;3&#xff09;不能是关键字或保留字 例如&#xff1a; demo.class.exec1 //错误class…

Redis学习笔记(三)

Jedis java语言连接redis工具准备工作 下载地址&#xff1a;https://mvnrepository.com/artifact/redis.clients/jedis基于maven <dependency> <groupId> redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</versi…

【面试题】面试官:你能自己实现一个async await吗?

开启掘金成长之旅&#xff01;这是我参与「掘金日新计划 12 月更文挑战」的第3天(点击查看活动详情) 相信大家对于Promise都不再陌生了&#xff0c;简易版的Promise对象源码我们也手撕过一次了&#xff0c;那接下来我们聊聊Promise的语法糖async-await&#xff0c;那让我们从…

【JavaWeb】第六章 xml

文章目录1、XML简介2、xml语法3、xml解析4、Dom4j类库的使用5、dom4j解析xml1、XML简介 xml是可扩展的标记性语言&#xff0c;xml的主要作用有&#xff1a; 用来保存数据&#xff0c;而且这些数据具有自我描述性 做为项目或者模块的配置文件做为网络传输数据的格式&#xff0…

QML 如何显示文本?Text可以有多少功能?

目录1.如何显示文本&#xff1f;2. Text有哪些主要功能&#xff1f;2.1 基本属性示例2.2 字重属性2.3 字体样式2.4 字体上标下标支持2.5 富文本2.6 文字换行 缩略1.如何显示文本&#xff1f; Text {font.pixelSize: 20; text: "这是20普通文字"} //一行即可以上代码…

12.2排序

目录 0.做题的失误 1.引用传值和传址 1.斐波那契数列 一.快速排序 1.挖坑法 2.优化 2.1 随机取数法 2.2 三数取中法 2.3把基准值相同的值移到基准旁边 2.4引用直接插入排序 3.Hoare 法: 4.非递归法 5.总结 二,归并排序 1.原理 2.代码实现 3.分析 4.非递归 5…

Git(第一篇)——Git的下载与安装(史上最全最详细)

Git&#xff08;第一篇&#xff09;——Git的下载与安装&#xff08;史上最全最详细&#xff09; 目录Git&#xff08;第一篇&#xff09;——Git的下载与安装&#xff08;史上最全最详细&#xff09;git的下载git的安装git的下载 如果你还没有下载Git&#xff0c;可直接到git…

什么是数据管理能力成熟度评估(DCMM)

GB/T 36073-2018 《数据管理能力成熟度评估模型》&#xff08;Data Management Capability Maturity Assessment Model&#xff0c;简称&#xff1a;DCMM&#xff09;是我国数据管理领域首个国家标准。该标准将组织对象的数据管理划分为八大能力域&#xff08;数据战略、数据治…

【Hbase】第一章——从原理剖析

文章目录1. HBase的实现原理1.1 HBase功能组件1.2 表和Region1.3 Region的定位2. HBase运行机制2.1 HBase系统架构2.2 Region服务器工作原理2.3 Store工作原理2.4 HLog工作原理3. HBase应用方案3.1 HBase实际应用中的性能优化方法3.2 HBase性能监视3.3 在HBase之上构建SQL引擎3…

【图像压缩】DCT图像无损压缩【含GUI Matlab源码 726期】

⛄一、DCT图像无损压缩简介 1 图像压缩 图像压缩按照压缩过程中是否有信息的损失以及解压后与原始图像是否有误差可以分为无损压缩和有损压缩两大类。无损压缩是指不损失图像质量的压缩&#xff0c;它是对文件的存储方式进行优化&#xff0c;采用某种算法表示重复的数据信息&a…