Skip to main content

poulpy_core/layouts/
glwe_switching_key.rs

1use poulpy_hal::{
2    layouts::{Backend, Data, FillUniform, HostDataMut, HostDataRef, ReaderFrom, WriterTo},
3    source::Source,
4};
5
6use crate::{
7    DeclaredK,
8    layouts::{
9        Base2K, Degree, Dnum, Dsize, GGLWE, GGLWEAtViewMut, GGLWEAtViewRef, GGLWEBackendMut, GGLWEBackendRef, GGLWEInfos,
10        GGLWEToBackendMut, GGLWEToBackendRef, GLWE, GLWEInfos, GLWEViewMut, GLWEViewRef, LWEInfos, Rank, TorusPrecision,
11    },
12};
13use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
14
15use std::fmt;
16
17/// Plain-data descriptor for a [`GLWESwitchingKey`] carrying only the
18/// layout parameters (no backing buffer).
19///
20/// Implements [`LWEInfos`], [`GLWEInfos`] and [`GGLWEInfos`] so it can
21/// be passed to any generic constructor that needs layout information.
22#[derive(PartialEq, Eq, Copy, Clone, Debug)]
23pub struct GLWESwitchingKeyLayout {
24    pub n: Degree,
25    pub base2k: Base2K,
26    pub k: TorusPrecision,
27    pub rank_in: Rank,
28    pub rank_out: Rank,
29    pub dnum: Dnum,
30    pub dsize: Dsize,
31}
32
33impl DeclaredK for GLWESwitchingKeyLayout {
34    fn k(&self) -> TorusPrecision {
35        self.k
36    }
37}
38
39impl LWEInfos for GLWESwitchingKeyLayout {
40    fn n(&self) -> Degree {
41        self.n
42    }
43
44    fn base2k(&self) -> Base2K {
45        self.base2k
46    }
47
48    fn size(&self) -> usize {
49        self.k.as_usize().div_ceil(self.base2k.as_usize())
50    }
51}
52
53impl GLWEInfos for GLWESwitchingKeyLayout {
54    fn rank(&self) -> Rank {
55        self.rank_out()
56    }
57}
58
59impl GGLWEInfos for GLWESwitchingKeyLayout {
60    fn rank_in(&self) -> Rank {
61        self.rank_in
62    }
63
64    fn rank_out(&self) -> Rank {
65        self.rank_out
66    }
67
68    fn dsize(&self) -> Dsize {
69        self.dsize
70    }
71
72    fn dnum(&self) -> Dnum {
73        self.dnum
74    }
75}
76
77/// GLWE key-switching key.
78///
79/// Wraps a [`GGLWE`] and additionally stores the polynomial degrees of
80/// the input and output secret keys (`input_degree` / `output_degree`).
81///
82/// `D: Data` is the backing storage type (e.g. `Vec<u8>`, `&[u8]`,
83/// `&mut [u8]`).
84#[derive(PartialEq, Eq, Clone)]
85pub struct GLWESwitchingKey<D: Data> {
86    pub(crate) key: GGLWE<D>,
87    pub(crate) input_degree: Degree,  // Degree of sk_in
88    pub(crate) output_degree: Degree, // Degree of sk_out
89}
90
91/// Provides read access to the input and output secret-key degrees
92/// stored in a [`GLWESwitchingKey`].
93pub trait GLWESwitchingKeyDegrees {
94    /// Returns the polynomial degree of the input secret key.
95    fn input_degree(&self) -> &Degree;
96    /// Returns the polynomial degree of the output secret key.
97    fn output_degree(&self) -> &Degree;
98}
99
100impl<D: HostDataRef> GLWESwitchingKeyDegrees for GLWESwitchingKey<D> {
101    fn output_degree(&self) -> &Degree {
102        &self.output_degree
103    }
104
105    fn input_degree(&self) -> &Degree {
106        &self.input_degree
107    }
108}
109
110/// Provides mutable access to the input and output secret-key degrees
111/// stored in a [`GLWESwitchingKey`].
112pub trait GLWESwitchingKeyDegreesMut {
113    /// Returns a mutable reference to the input secret-key degree.
114    fn input_degree(&mut self) -> &mut Degree;
115    /// Returns a mutable reference to the output secret-key degree.
116    fn output_degree(&mut self) -> &mut Degree;
117}
118
119impl<D: HostDataMut> GLWESwitchingKeyDegreesMut for GLWESwitchingKey<D> {
120    fn output_degree(&mut self) -> &mut Degree {
121        &mut self.output_degree
122    }
123
124    fn input_degree(&mut self) -> &mut Degree {
125        &mut self.input_degree
126    }
127}
128
129impl<D: Data> LWEInfos for GLWESwitchingKey<D> {
130    fn n(&self) -> Degree {
131        self.key.n()
132    }
133
134    fn base2k(&self) -> Base2K {
135        self.key.base2k()
136    }
137
138    fn size(&self) -> usize {
139        self.key.size()
140    }
141}
142
143impl<D: Data> GLWEInfos for GLWESwitchingKey<D> {
144    fn rank(&self) -> Rank {
145        self.rank_out()
146    }
147}
148
149impl<D: Data> GGLWEInfos for GLWESwitchingKey<D> {
150    fn rank_in(&self) -> Rank {
151        self.key.rank_in()
152    }
153
154    fn rank_out(&self) -> Rank {
155        self.key.rank_out()
156    }
157
158    fn dsize(&self) -> Dsize {
159        self.key.dsize()
160    }
161
162    fn dnum(&self) -> Dnum {
163        self.key.dnum()
164    }
165}
166
167impl<D: HostDataRef> fmt::Debug for GLWESwitchingKey<D> {
168    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169        write!(f, "{self}")
170    }
171}
172
173impl<D: HostDataRef> fmt::Display for GLWESwitchingKey<D> {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        write!(
176            f,
177            "(GLWESwitchingKey: sk_in_n={} sk_out_n={}) {}",
178            self.input_degree,
179            self.output_degree,
180            self.key.data()
181        )
182    }
183}
184
185impl<D: HostDataMut> FillUniform for GLWESwitchingKey<D> {
186    fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
187        self.key.fill_uniform(log_bound, source);
188    }
189}
190
191#[expect(
192    dead_code,
193    reason = "host-owned constructors are kept for serialization and host-only staging"
194)]
195impl GLWESwitchingKey<Vec<u8>> {
196    /// Allocates a new [`GLWESwitchingKey`] with the given parameters.
197    pub(crate) fn alloc_from_infos<A>(infos: &A) -> Self
198    where
199        A: GGLWEInfos,
200    {
201        Self::alloc(
202            infos.n(),
203            infos.base2k(),
204            infos.max_k(),
205            infos.rank_in(),
206            infos.rank_out(),
207            infos.dnum(),
208            infos.dsize(),
209        )
210    }
211
212    /// Allocates a new [`GLWESwitchingKey`] with the given parameters.
213    pub(crate) fn alloc(
214        n: Degree,
215        base2k: Base2K,
216        k: TorusPrecision,
217        rank_in: Rank,
218        rank_out: Rank,
219        dnum: Dnum,
220        dsize: Dsize,
221    ) -> Self {
222        GLWESwitchingKey {
223            key: GGLWE::alloc(n, base2k, k, rank_in, rank_out, dnum, dsize),
224            input_degree: Degree(0),
225            output_degree: Degree(0),
226        }
227    }
228
229    /// Returns the byte count required for a [`GLWESwitchingKey`] with the given parameters.
230    pub fn bytes_of_from_infos<A>(infos: &A) -> usize
231    where
232        A: GGLWEInfos,
233    {
234        Self::bytes_of(
235            infos.n(),
236            infos.base2k(),
237            infos.max_k(),
238            infos.rank_in(),
239            infos.rank_out(),
240            infos.dnum(),
241            infos.dsize(),
242        )
243    }
244
245    /// Returns the byte count required for a [`GLWESwitchingKey`] with the given parameters.
246    pub fn bytes_of(
247        n: Degree,
248        base2k: Base2K,
249        k: TorusPrecision,
250        rank_in: Rank,
251        rank_out: Rank,
252        dnum: Dnum,
253        dsize: Dsize,
254    ) -> usize {
255        GGLWE::bytes_of(n, base2k, k, rank_in, rank_out, dnum, dsize)
256    }
257}
258
259impl_gglwe_to_backend_for_field!(GLWESwitchingKey<D>, key, GGLWE<D>);
260
261impl_gglwe_at_view_for_field!(GLWESwitchingKey<BE::OwnedBuf>; key);
262
263impl_glwe_host_at_for_field!(GLWESwitchingKey<D>; key);
264
265impl<D: HostDataMut> ReaderFrom for GLWESwitchingKey<D> {
266    /// Deserialises from little-endian binary format.
267    fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
268        self.input_degree = Degree(reader.read_u32::<LittleEndian>()?);
269        self.output_degree = Degree(reader.read_u32::<LittleEndian>()?);
270        self.key.read_from(reader)
271    }
272}
273
274impl<D: HostDataRef> WriterTo for GLWESwitchingKey<D> {
275    /// Serialises in little-endian binary format.
276    fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
277        writer.write_u32::<LittleEndian>(self.input_degree.into())?;
278        writer.write_u32::<LittleEndian>(self.output_degree.into())?;
279        self.key.write_to(writer)
280    }
281}