fullcodec_plonk/plonkup/
multiset.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use crate::error::Error;
8use crate::fft::{EvaluationDomain, Polynomial};
9use core::ops::{Add, Mul};
10use dusk_bls12_381::BlsScalar;
11use dusk_bytes::{DeserializableSlice, Serializable};
12use sp_std::vec;
13use sp_std::vec::Vec;
14
15/// MultiSet is struct containing vectors of scalars, which
16/// individually represents either a wire value or an index
17/// of a Plonkup table
18#[derive(Clone, Eq, PartialEq, Debug)]
19pub struct MultiSet(pub Vec<BlsScalar>);
20
21impl Default for MultiSet {
22    fn default() -> Self {
23        MultiSet::new()
24    }
25}
26
27impl From<&[BlsScalar]> for MultiSet {
28    fn from(slice: &[BlsScalar]) -> MultiSet {
29        MultiSet(slice.to_vec())
30    }
31}
32
33impl MultiSet {
34    /// Creates an empty vector with a multiset wrapper around it
35    pub fn new() -> MultiSet {
36        MultiSet(vec![])
37    }
38
39    /// Generate a `MultiSet` struct from a slice of bytes.
40    pub fn from_slice(bytes: &[u8]) -> Result<MultiSet, Error> {
41        let elements = bytes
42            .chunks(BlsScalar::SIZE)
43            .map(|chunk| BlsScalar::from_slice(chunk))
44            .collect::<Result<Vec<BlsScalar>, dusk_bytes::Error>>()?;
45
46        Ok(MultiSet(elements))
47    }
48
49    /// Given a [`MultiSet`], return it in it's bytes representation
50    /// element by element.
51    pub fn to_var_bytes(&self) -> Vec<u8> {
52        self.0
53            .iter()
54            .map(|item| item.to_bytes().to_vec())
55            .flatten()
56            .collect()
57    }
58
59    /// Extends the length of the multiset to n elements
60    /// The n will be the size of the arithmetic circuit.
61    /// This will extend the vectors to the size
62    pub fn pad(&mut self, n: u32) {
63        assert!(n.is_power_of_two());
64        let diff = n - self.len() as u32;
65        self.0.extend(vec![self.0[0]; diff as usize]);
66    }
67
68    /// Pushes chosen value onto the end of the Multiset
69    pub fn push(&mut self, value: BlsScalar) {
70        self.0.push(value)
71    }
72
73    /// Fetches last element in MultiSet.
74    /// Returns None if there are no elements in the MultiSet.
75    pub fn last(&self) -> Option<&BlsScalar> {
76        self.0.last()
77    }
78
79    /// Returns the cardinality of the multiset
80    pub fn len(&self) -> usize {
81        self.0.len()
82    }
83
84    /// Returns whether or not the multiset is empty.
85    pub fn is_empty(&self) -> bool {
86        self.0.is_empty()
87    }
88
89    /// Returns the position of the element in the Multiset.
90    /// Returns None if the element is not found.
91    pub fn position(&self, element: &BlsScalar) -> Option<usize> {
92        self.0.iter().position(|&x| x == *element)
93    }
94
95    /// Concatenates and sorts two Multisets together.
96    /// From the Plonkup paper, if we have t: {1,2,4,3}
97    /// and f: {2,3,4,1}.
98    /// We first check if all elements of f exist in t
99    /// Then we combine the multisets together and sort
100    /// their elements together. The final MultiSet will
101    /// look as follows, s: {1,1,2,2,3,3,4,4}
102    pub fn sorted_concat(&self, f: &MultiSet) -> Result<MultiSet, Error> {
103        let mut s = self.clone();
104        s.0.reserve(f.0.len());
105        for element in f.0.iter() {
106            let index = s.position(element).ok_or(Error::ElementNotIndexed)?;
107            s.0.insert(index, *element);
108        }
109
110        Ok(s)
111    }
112
113    /// Checks whether one mutltiset is a subset of another.
114    /// This function will be used to check if the all elements
115    /// in set f, from the paper, are contained inside t.
116    pub fn contains_all(&self, other: &MultiSet) -> bool {
117        other.0.iter().all(|item| self.contains(item))
118    }
119
120    /// Checks if an element is in the MultiSet
121    pub fn contains(&self, entry: &BlsScalar) -> bool {
122        self.0.contains(entry)
123    }
124
125    /// Splits a multiset into halves as specified by the paper
126    /// The last element of the first half should be the same
127    /// as the first element of the second half.
128    /// Since a multiset can never have an even cardinality, we
129    /// always split it in the way described above.
130    pub fn halve(&self) -> (MultiSet, MultiSet) {
131        let length = self.0.len();
132
133        let first_half = MultiSet::from(&self.0[0..=length / 2]);
134        let second_half = MultiSet::from(&self.0[length / 2..]);
135
136        (first_half, second_half)
137    }
138
139    /// Splits a multiset into alternating halves of the same length
140    /// as specified in the Plonkup paper. A multiset must have even
141    /// cardinality to be split in this manner.
142    pub fn halve_alternating(&self) -> (MultiSet, MultiSet) {
143        let mut evens = vec![];
144        let mut odds = vec![];
145        for i in 0..self.len() {
146            if i % 2 == 0 {
147                evens.push(self.0[i]);
148            } else {
149                odds.push(self.0[i]);
150            }
151        }
152
153        (MultiSet(evens), MultiSet(odds))
154    }
155
156    /// Treats each element in the multiset as evaluation points
157    /// Computes IFFT of the set of evaluation points
158    /// and returns the coefficients as a Polynomial data structure
159    pub(crate) fn to_polynomial(
160        &self,
161        domain: &EvaluationDomain,
162    ) -> Polynomial {
163        Polynomial::from_coefficients_vec(domain.ifft(&self.0))
164    }
165
166    /// Turn three multisets into a single multiset using
167    /// a random challenge, Alpha. Alpha is dervived by hashing
168    /// the transcript.
169    /// The function iterates over the given sets and mutiplies by alpha:
170    /// a + (b * alpha) + (c * alpha^2)  
171    pub fn compress_three_arity(
172        multisets: [&MultiSet; 3],
173        alpha: BlsScalar,
174    ) -> MultiSet {
175        MultiSet(
176            multisets[0]
177                .0
178                .iter()
179                .zip(multisets[1].0.iter())
180                .zip(multisets[2].0.iter())
181                .map(|((a, b), c)| a + b * alpha + c * alpha.square())
182                .collect::<Vec<BlsScalar>>(),
183        )
184    }
185
186    /// Turn four multisets into a single multiset using
187    /// a random challenge, Alpha. Alpha is dervived by hashing
188    /// the transcript.
189    /// The function iterates over the given sets and mutiplies by alpha:
190    /// a + (b * alpha) + (c * alpha^2) + (d * alpha^3)  
191    pub fn compress_four_arity(
192        multisets: [&MultiSet; 4],
193        alpha: BlsScalar,
194    ) -> MultiSet {
195        MultiSet(
196            multisets[0]
197                .0
198                .iter()
199                .zip(multisets[1].0.iter())
200                .zip(multisets[2].0.iter())
201                .zip(multisets[3].0.iter())
202                .map(|(((a, b), c), d)| {
203                    a + b * alpha
204                        + c * alpha.square()
205                        + d * alpha.pow(&[3u64, 0u64, 0u64, 0u64])
206                })
207                .collect::<Vec<BlsScalar>>(),
208        )
209    }
210}
211
212impl Add for MultiSet {
213    type Output = MultiSet;
214
215    fn add(self, other: MultiSet) -> Self::Output {
216        let result = self
217            .0
218            .into_iter()
219            .zip(other.0.iter())
220            .map(|(x, y)| x + y)
221            .collect();
222
223        MultiSet(result)
224    }
225}
226
227impl Mul for MultiSet {
228    type Output = MultiSet;
229
230    fn mul(self, other: MultiSet) -> Self::Output {
231        let result = self
232            .0
233            .into_iter()
234            .zip(other.0.iter())
235            .map(|(x, y)| x * y)
236            .collect();
237
238        MultiSet(result)
239    }
240}
241
242#[cfg(test)]
243mod test {
244    use super::*;
245    use crate::fft::EvaluationDomain;
246    use crate::plonkup::WitnessTable;
247
248    #[test]
249    fn test_halve() {
250        let mut s = MultiSet::new();
251        s.push(BlsScalar::from(0));
252        s.push(BlsScalar::from(1));
253        s.push(BlsScalar::from(2));
254        s.push(BlsScalar::from(3));
255        s.push(BlsScalar::from(4));
256        s.push(BlsScalar::from(5));
257        s.push(BlsScalar::from(6));
258
259        let (h_1, h_2) = s.halve();
260        assert_eq!(h_1.len(), 4);
261        assert_eq!(h_2.len(), 4);
262
263        let left_half = MultiSet(vec![
264            BlsScalar::from(0),
265            BlsScalar::from(1),
266            BlsScalar::from(2),
267            BlsScalar::from(3),
268        ]);
269
270        assert_eq!(left_half, h_1);
271
272        let right_half = MultiSet(vec![
273            BlsScalar::from(3),
274            BlsScalar::from(4),
275            BlsScalar::from(5),
276            BlsScalar::from(6),
277        ]);
278
279        assert_eq!(right_half, h_2);
280
281        // The last element of the first half should equal the first
282        // element of the second half.
283        assert_eq!(h_1.0.last().unwrap(), &h_2.0[0])
284    }
285
286    #[test]
287    fn test_to_polynomial() {
288        let mut s = MultiSet::new();
289        s.push(BlsScalar::from(1));
290        s.push(BlsScalar::from(2));
291        s.push(BlsScalar::from(3));
292        s.push(BlsScalar::from(4));
293        s.push(BlsScalar::from(5));
294        s.push(BlsScalar::from(6));
295        s.push(BlsScalar::from(7));
296
297        let domain = EvaluationDomain::new(s.len() + 1).unwrap();
298        let s_poly = s.to_polynomial(&domain);
299
300        assert_eq!(s_poly.degree(), 7)
301    }
302    #[test]
303    fn test_is_subset() {
304        let mut t = MultiSet::new();
305        t.push(BlsScalar::from(1));
306        t.push(BlsScalar::from(2));
307        t.push(BlsScalar::from(3));
308        t.push(BlsScalar::from(4));
309        t.push(BlsScalar::from(5));
310        t.push(BlsScalar::from(6));
311        t.push(BlsScalar::from(7));
312        let mut f = MultiSet::new();
313        f.push(BlsScalar::from(1));
314        f.push(BlsScalar::from(2));
315        let mut n = MultiSet::new();
316        n.push(BlsScalar::from(8));
317
318        assert!(t.contains_all(&f));
319        assert!(!t.contains_all(&n));
320    }
321
322    #[test]
323    fn test_full_compression_into_s() {
324        let mut t = MultiSet::new();
325
326        t.push(BlsScalar::zero());
327        t.push(BlsScalar::one());
328        t.push(BlsScalar::from(2));
329        t.push(BlsScalar::from(3));
330        t.push(BlsScalar::from(4));
331        t.push(BlsScalar::from(5));
332        t.push(BlsScalar::from(6));
333        t.push(BlsScalar::from(7));
334
335        let mut f = MultiSet::new();
336        f.push(BlsScalar::from(3));
337        f.push(BlsScalar::from(6));
338        f.push(BlsScalar::from(0));
339        f.push(BlsScalar::from(5));
340        f.push(BlsScalar::from(4));
341        f.push(BlsScalar::from(3));
342        f.push(BlsScalar::from(2));
343        f.push(BlsScalar::from(0));
344        f.push(BlsScalar::from(0));
345        f.push(BlsScalar::from(1));
346        f.push(BlsScalar::from(2));
347
348        assert!(t.contains_all(&f));
349
350        assert!(t.contains(&BlsScalar::from(2)));
351
352        let s = t.sorted_concat(&f);
353
354        // The sets should be merged but also
355        // in the ascending order
356        let concatenated_set = MultiSet(vec![
357            BlsScalar::zero(),
358            BlsScalar::zero(),
359            BlsScalar::zero(),
360            BlsScalar::zero(),
361            BlsScalar::one(),
362            BlsScalar::one(),
363            BlsScalar::from(2),
364            BlsScalar::from(2),
365            BlsScalar::from(2),
366            BlsScalar::from(3),
367            BlsScalar::from(3),
368            BlsScalar::from(3),
369            BlsScalar::from(4),
370            BlsScalar::from(4),
371            BlsScalar::from(5),
372            BlsScalar::from(5),
373            BlsScalar::from(6),
374            BlsScalar::from(6),
375            BlsScalar::from(7),
376        ]);
377
378        assert_eq!(s.unwrap(), concatenated_set);
379    }
380
381    #[test]
382    fn multiset_compression_input() {
383        // Alpha is a random challenge from
384        // the transcript
385        let alpha = BlsScalar::from(2);
386        let alpha_squared = alpha * alpha;
387
388        let mut table = WitnessTable::default();
389
390        // Fill in wires directly, no need to use a
391        // plonkup table as this will not be going
392        // into a proof
393        table.from_wire_values(
394            BlsScalar::from(1),
395            BlsScalar::from(2),
396            BlsScalar::from(3),
397            BlsScalar::from(4),
398        );
399
400        // Computed expected result
401        let compressed_element = MultiSet::compress_three_arity(
402            [&table.f_1, &table.f_2, &table.f_3],
403            alpha,
404        );
405
406        let actual_element = BlsScalar::from(1)
407            + (BlsScalar::from(2) * alpha)
408            + (BlsScalar::from(3) * alpha_squared);
409
410        let mut actual_set = MultiSet::new();
411
412        actual_set.push(actual_element);
413
414        assert_eq!(actual_set, compressed_element);
415    }
416}