poulpy_core/default/linear_transformation/
prepare.rs1use poulpy_hal::layouts::CnvPVecRToBackendMut;
14use poulpy_hal::{
15 api::{CnvPVecAlloc, Convolution},
16 layouts::{Backend, ScratchArena},
17};
18
19use crate::layouts::IntPolyInfos;
20use crate::{
21 default::operations::msb_mask_bottom_limb,
22 layouts::{
23 GLWEInfos, GLWEToBackendRef, LWEInfos, LinearTransformation, LinearTransformationDiagonal, LinearTransformationGiantStep,
24 LinearTransformationLayout, LinearTransformationPlan, prepared::PreparedDiagonal,
25 },
26};
27
28impl<BE: Backend> LinearTransformation<PreparedDiagonal<BE::OwnedBuf, BE>> {
29 pub fn alloc_prepared<M, P>(module: &M, layout: &LinearTransformationLayout, pt_infos: &P) -> Self
35 where
36 M: CnvPVecAlloc<BE>,
37 P: LWEInfos,
38 {
39 Self::alloc_prepared_from_index(module, &layout.index(), pt_infos)
40 }
41
42 pub fn alloc_prepared_from_index<M, P>(module: &M, index: &LinearTransformationPlan, pt_infos: &P) -> Self
49 where
50 M: CnvPVecAlloc<BE>,
51 P: LWEInfos,
52 {
53 let pt_size = pt_infos.size();
54 let base2k = pt_infos.base2k();
55 let k = pt_infos.k();
56
57 let mut giant_steps = Vec::with_capacity(index.giant_steps.len());
58 for (g, &rot) in index.giant_steps.iter().enumerate() {
59 let baby_rots = &index.index[g];
60 let mut diagonals = Vec::with_capacity(baby_rots.len());
61 for &baby in baby_rots {
62 diagonals.push(LinearTransformationDiagonal {
63 baby,
64 plaintext: PreparedDiagonal {
65 cnv: module.cnv_pvec_right_alloc(1, pt_size),
66 base2k,
67 k,
68 log_scale: 0,
69 },
70 });
71 }
72 giant_steps.push(LinearTransformationGiantStep { rot, diagonals });
73 }
74
75 LinearTransformation {
76 baby_steps: index.baby_steps.clone(),
77 giant_steps,
78 }
79 }
80
81 pub fn set_log_scale(&mut self, log_scale: usize) {
85 for gs in &mut self.giant_steps {
86 for d in &mut gs.diagonals {
87 d.plaintext.set_log_scale(log_scale);
88 }
89 }
90 }
91
92 pub fn log_scale(&self) -> usize {
94 self.first_diagonal_plaintext()
95 .expect("prepared linear transformation has no diagonals")
96 .log_scale()
97 }
98}
99
100pub fn glwe_prepare_linear_transformation_rhs_tmp_bytes_default<BE, M, P>(module: &M, pt_infos: &P) -> usize
102where
103 BE: Backend,
104 M: Convolution<BE>,
105 P: LWEInfos,
106{
107 module.cnv_prepare_right_tmp_bytes(pt_infos.size(), pt_infos.size())
108}
109
110pub fn glwe_prepare_linear_transformation_rhs_default<BE, M, P>(
118 module: &M,
119 prepared: &mut LinearTransformation<PreparedDiagonal<BE::OwnedBuf, BE>>,
120 lt: &LinearTransformation<P>,
121 scratch: &mut ScratchArena<'_, BE>,
122) where
123 BE: Backend,
124 M: CnvPVecAlloc<BE> + Convolution<BE>,
125 P: GLWEToBackendRef<BE> + GLWEInfos,
126{
127 if !lt.baby_steps.is_empty() {
128 assert_eq!(
129 lt.baby_steps.first(),
130 Some(&0),
131 "baby_steps must start with the identity rotation (0)"
132 );
133 }
134
135 let first = prepared
136 .first_diagonal_plaintext()
137 .expect("prepared linear transformation has no diagonals");
138 let pt_base2k = first.base2k();
139 let pt_k = first.k();
140 let pt_base2k_usize = pt_base2k.as_usize();
141 let pt_k_usize = pt_k.as_usize();
142 let mask = msb_mask_bottom_limb(pt_base2k_usize, first.encoded_k().as_usize());
146
147 for gs in <.giant_steps {
148 if gs.diagonals.is_empty() {
149 continue;
150 }
151 let prepared_gs = prepared
152 .giant_steps
153 .iter_mut()
154 .find(|p| p.rot == gs.rot)
155 .unwrap_or_else(|| panic!("prepared cache has no giant step for rotation {}", gs.rot));
156
157 for d in &gs.diagonals {
158 let plaintext = &d.plaintext;
159 assert_eq!(
160 plaintext.base2k(),
161 pt_base2k,
162 "linear transformation diagonal base2k does not match prepared cache"
163 );
164 assert_eq!(
165 plaintext.k(),
166 pt_k,
167 "linear transformation diagonal k does not match prepared cache"
168 );
169 assert_eq!(
170 pt_k_usize.div_ceil(pt_base2k_usize),
171 plaintext.size(),
172 "linear transformation plaintext size does not match its effective precision"
173 );
174
175 let prepared_slot = prepared_gs
176 .diagonals
177 .iter_mut()
178 .find(|p| p.baby == d.baby)
179 .unwrap_or_else(|| panic!("prepared cache has no diagonal slot for baby {} at giant {}", d.baby, gs.rot));
180 let plaintext_backend = plaintext.to_backend_ref();
181 module.cnv_prepare_right(
182 &mut prepared_slot.plaintext.cnv_mut().to_backend_mut(),
183 &plaintext_backend.data,
184 mask,
185 scratch,
186 );
187 }
188 }
189}