circomspect_program_structure/intermediate_representation/
degree_meta.rs

1use log::trace;
2use std::cmp::{Ordering, min, max};
3use std::collections::HashMap;
4use std::fmt;
5
6use super::{VariableName, VariableType};
7
8/// The degree of an expression.
9#[derive(Clone, Copy, PartialEq, Eq, Hash)]
10pub enum Degree {
11    Constant,
12    Linear,
13    Quadratic,
14    NonQuadratic,
15}
16
17// Degrees are linearly ordered.
18impl PartialOrd<Degree> for Degree {
19    fn partial_cmp(&self, other: &Degree) -> Option<Ordering> {
20        Some(self.cmp(other))
21    }
22}
23
24// Degrees are linearly ordered.
25impl Ord for Degree {
26    fn cmp(&self, other: &Degree) -> Ordering {
27        use Degree::*;
28        match (self, other) {
29            // `Constant <= _`
30            (Constant, Constant) => Ordering::Equal,
31            (Constant, Linear) | (Constant, Quadratic) | (Constant, NonQuadratic) => Ordering::Less,
32            // `Linear <= _`
33            (Linear, Linear) => Ordering::Equal,
34            (Linear, Quadratic) | (Linear, NonQuadratic) => Ordering::Less,
35            // `Quadratic <= _`
36            (Quadratic, Quadratic) => Ordering::Equal,
37            (Quadratic, NonQuadratic) => Ordering::Less,
38            // `NonQuadratic <= _`
39            (NonQuadratic, NonQuadratic) => Ordering::Equal,
40            // All other cases are on the form `_ >= _`.
41            _ => Ordering::Greater,
42        }
43    }
44}
45
46impl Degree {
47    pub fn add(&self, other: &Degree) -> Degree {
48        max(*self, *other)
49    }
50
51    pub fn infix_sub(&self, other: &Degree) -> Degree {
52        max(*self, *other)
53    }
54
55    pub fn mul(&self, other: &Degree) -> Degree {
56        use Degree::*;
57        match (self, other) {
58            (Constant, _) => *other,
59            (_, Constant) => *self,
60            (Linear, Linear) => Quadratic,
61            _ => NonQuadratic,
62        }
63    }
64
65    pub fn pow(&self, other: &Degree) -> Degree {
66        use Degree::*;
67        if (*self, *other) == (Constant, Constant) {
68            Constant
69        } else {
70            NonQuadratic
71        }
72    }
73
74    pub fn div(&self, other: &Degree) -> Degree {
75        use Degree::*;
76        if *other == Constant {
77            *self
78        } else {
79            NonQuadratic
80        }
81    }
82
83    pub fn int_div(&self, other: &Degree) -> Degree {
84        use Degree::*;
85        if (*self, *other) == (Constant, Constant) {
86            Constant
87        } else {
88            NonQuadratic
89        }
90    }
91
92    pub fn modulo(&self, other: &Degree) -> Degree {
93        use Degree::*;
94        if (*self, *other) == (Constant, Constant) {
95            Constant
96        } else {
97            NonQuadratic
98        }
99    }
100
101    pub fn shift_left(&self, other: &Degree) -> Degree {
102        use Degree::*;
103        if (*self, *other) == (Constant, Constant) {
104            Constant
105        } else {
106            NonQuadratic
107        }
108    }
109
110    pub fn shift_right(&self, other: &Degree) -> Degree {
111        use Degree::*;
112        if (*self, *other) == (Constant, Constant) {
113            Constant
114        } else {
115            NonQuadratic
116        }
117    }
118
119    pub fn lesser(&self, other: &Degree) -> Degree {
120        use Degree::*;
121        if (*self, *other) == (Constant, Constant) {
122            Constant
123        } else {
124            NonQuadratic
125        }
126    }
127
128    pub fn greater(&self, other: &Degree) -> Degree {
129        use Degree::*;
130        if (*self, *other) == (Constant, Constant) {
131            Constant
132        } else {
133            NonQuadratic
134        }
135    }
136
137    pub fn lesser_eq(&self, other: &Degree) -> Degree {
138        use Degree::*;
139        if (*self, *other) == (Constant, Constant) {
140            Constant
141        } else {
142            NonQuadratic
143        }
144    }
145
146    pub fn greater_eq(&self, other: &Degree) -> Degree {
147        use Degree::*;
148        if (*self, *other) == (Constant, Constant) {
149            Constant
150        } else {
151            NonQuadratic
152        }
153    }
154    pub fn equal(&self, other: &Degree) -> Degree {
155        use Degree::*;
156        if (*self, *other) == (Constant, Constant) {
157            Constant
158        } else {
159            NonQuadratic
160        }
161    }
162
163    pub fn not_equal(&self, other: &Degree) -> Degree {
164        use Degree::*;
165        if (*self, *other) == (Constant, Constant) {
166            Constant
167        } else {
168            NonQuadratic
169        }
170    }
171
172    pub fn bit_or(&self, other: &Degree) -> Degree {
173        use Degree::*;
174        if (*self, *other) == (Constant, Constant) {
175            Constant
176        } else {
177            NonQuadratic
178        }
179    }
180
181    pub fn bit_and(&self, other: &Degree) -> Degree {
182        use Degree::*;
183        if (*self, *other) == (Constant, Constant) {
184            Constant
185        } else {
186            NonQuadratic
187        }
188    }
189
190    pub fn bit_xor(&self, other: &Degree) -> Degree {
191        use Degree::*;
192        if (*self, *other) == (Constant, Constant) {
193            Constant
194        } else {
195            NonQuadratic
196        }
197    }
198
199    pub fn bool_or(&self, other: &Degree) -> Degree {
200        use Degree::*;
201        if (*self, *other) == (Constant, Constant) {
202            Constant
203        } else {
204            NonQuadratic
205        }
206    }
207
208    pub fn bool_and(&self, other: &Degree) -> Degree {
209        use Degree::*;
210        if (*self, *other) == (Constant, Constant) {
211            Constant
212        } else {
213            NonQuadratic
214        }
215    }
216
217    pub fn prefix_sub(&self) -> Degree {
218        *self
219    }
220
221    pub fn complement(&self) -> Degree {
222        use Degree::*;
223        Quadratic
224    }
225
226    pub fn bool_not(&self) -> Degree {
227        use Degree::*;
228        Quadratic
229    }
230}
231
232impl fmt::Debug for Degree {
233    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
234        use Degree::*;
235        match self {
236            Constant => write!(f, "constant"),
237            Linear => write!(f, "linear"),
238            Quadratic => write!(f, "quadratic"),
239            NonQuadratic => write!(f, "non-quadratic"),
240        }
241    }
242}
243
244/// An inclusive range of degrees.
245#[derive(Clone, PartialEq, Eq, Hash)]
246pub struct DegreeRange(Degree, Degree);
247
248impl DegreeRange {
249    #[must_use]
250    pub fn new(start: Degree, end: Degree) -> DegreeRange {
251        DegreeRange(start, end)
252    }
253
254    #[must_use]
255    pub fn start(&self) -> Degree {
256        self.0
257    }
258
259    #[must_use]
260    pub fn end(&self) -> Degree {
261        self.1
262    }
263
264    #[must_use]
265    pub fn contains(&self, degree: Degree) -> bool {
266        self.start() <= degree && degree <= self.end()
267    }
268
269    /// Returns true if the upper bound is at most constant.
270    #[must_use]
271    pub fn is_constant(&self) -> bool {
272        self.end() <= Degree::Constant
273    }
274
275    /// Returns true if the upper bound is at most linear.
276    #[must_use]
277    pub fn is_linear(&self) -> bool {
278        self.end() <= Degree::Linear
279    }
280
281    /// Returns true if the upper bound is at most quadratic.
282    #[must_use]
283    pub fn is_quadratic(&self) -> bool {
284        self.end() <= Degree::Quadratic
285    }
286
287    /// Computes the infimum (under inverse inclusion) of `self` and `other`.
288    /// Note, if the two ranges overlap this will simply be the union of `self`
289    /// and `other`.
290    pub fn inf(&self, other: &DegreeRange) -> DegreeRange {
291        DegreeRange(min(self.start(), other.start()), max(self.end(), other.end()))
292    }
293
294    /// Constructs the infimum (under inverse inclusion) of the given degree ranges.
295    /// Note, if the ranges overlap this will simply be the union of all the ranges.
296    ///
297    /// # Panics
298    ///
299    /// This method will panic if the iterator is empty.
300    pub fn iter_inf<'a, T: IntoIterator<Item = &'a DegreeRange>>(ranges: T) -> DegreeRange {
301        let mut ranges = ranges.into_iter();
302        if let Some(range) = ranges.next() {
303            let mut result = range.clone();
304            for range in ranges {
305                result = result.inf(range);
306            }
307            result
308        } else {
309            panic!("iterator must not be empty")
310        }
311    }
312
313    // If the iterator is not empty and all the ranges are `Some(range)` this
314    // method will return the same as `DegreeRange::iter_inf`, otherwise it will
315    // return `None`.
316    pub fn iter_opt<'a, T: IntoIterator<Item = Option<&'a DegreeRange>>>(
317        ranges: T,
318    ) -> Option<DegreeRange> {
319        let ranges = ranges.into_iter().collect::<Option<Vec<_>>>();
320        match ranges {
321            Some(ranges) if !ranges.is_empty() => Some(Self::iter_inf(ranges)),
322            _ => None,
323        }
324    }
325
326    pub fn add(&self, other: &DegreeRange) -> DegreeRange {
327        DegreeRange::new(self.start().add(&other.start()), self.end().add(&other.end()))
328    }
329
330    pub fn infix_sub(&self, other: &DegreeRange) -> DegreeRange {
331        DegreeRange::new(self.start().infix_sub(&other.start()), self.end().infix_sub(&other.end()))
332    }
333
334    pub fn mul(&self, other: &DegreeRange) -> DegreeRange {
335        DegreeRange::new(self.start().mul(&other.start()), self.end().mul(&other.end()))
336    }
337
338    pub fn pow(&self, other: &DegreeRange) -> DegreeRange {
339        DegreeRange::new(self.start().pow(&other.start()), self.end().pow(&other.end()))
340    }
341
342    pub fn div(&self, other: &DegreeRange) -> DegreeRange {
343        DegreeRange::new(self.start().div(&other.start()), self.end().div(&other.end()))
344    }
345
346    pub fn int_div(&self, other: &DegreeRange) -> DegreeRange {
347        DegreeRange::new(self.start().int_div(&other.start()), self.end().int_div(&other.end()))
348    }
349
350    pub fn modulo(&self, other: &DegreeRange) -> DegreeRange {
351        DegreeRange::new(self.start().modulo(&other.start()), self.end().modulo(&other.end()))
352    }
353
354    pub fn shift_left(&self, other: &DegreeRange) -> DegreeRange {
355        DegreeRange::new(
356            self.start().shift_left(&other.start()),
357            self.end().shift_left(&other.end()),
358        )
359    }
360
361    pub fn shift_right(&self, other: &DegreeRange) -> DegreeRange {
362        DegreeRange::new(
363            self.start().shift_right(&other.start()),
364            self.end().shift_right(&other.end()),
365        )
366    }
367
368    pub fn lesser(&self, other: &DegreeRange) -> DegreeRange {
369        DegreeRange::new(self.start().lesser(&other.start()), self.end().lesser(&other.end()))
370    }
371
372    pub fn greater(&self, other: &DegreeRange) -> DegreeRange {
373        DegreeRange::new(self.start().greater(&other.start()), self.end().greater(&other.end()))
374    }
375
376    pub fn lesser_eq(&self, other: &DegreeRange) -> DegreeRange {
377        DegreeRange::new(self.start().lesser_eq(&other.start()), self.end().lesser_eq(&other.end()))
378    }
379
380    pub fn greater_eq(&self, other: &DegreeRange) -> DegreeRange {
381        DegreeRange::new(
382            self.start().greater_eq(&other.start()),
383            self.end().greater_eq(&other.end()),
384        )
385    }
386    pub fn equal(&self, other: &DegreeRange) -> DegreeRange {
387        DegreeRange::new(self.start().equal(&other.start()), self.end().equal(&other.end()))
388    }
389
390    pub fn not_equal(&self, other: &DegreeRange) -> DegreeRange {
391        DegreeRange::new(self.start().not_equal(&other.start()), self.end().not_equal(&other.end()))
392    }
393
394    pub fn bit_or(&self, other: &DegreeRange) -> DegreeRange {
395        DegreeRange::new(self.start().bit_or(&other.start()), self.end().bit_or(&other.end()))
396    }
397
398    pub fn bit_and(&self, other: &DegreeRange) -> DegreeRange {
399        DegreeRange::new(self.start().bit_and(&other.start()), self.end().bit_and(&other.end()))
400    }
401
402    pub fn bit_xor(&self, other: &DegreeRange) -> DegreeRange {
403        DegreeRange::new(self.start().bit_xor(&other.start()), self.end().bit_xor(&other.end()))
404    }
405
406    pub fn bool_or(&self, other: &DegreeRange) -> DegreeRange {
407        DegreeRange::new(self.start().bool_or(&other.start()), self.end().bool_or(&other.end()))
408    }
409
410    pub fn bool_and(&self, other: &DegreeRange) -> DegreeRange {
411        DegreeRange::new(self.start().bool_and(&other.start()), self.end().bool_and(&other.end()))
412    }
413
414    pub fn prefix_sub(&self) -> DegreeRange {
415        DegreeRange::new(self.start().prefix_sub(), self.end().prefix_sub())
416    }
417
418    pub fn complement(&self) -> DegreeRange {
419        DegreeRange::new(self.start().complement(), self.end().complement())
420    }
421
422    pub fn bool_not(&self) -> DegreeRange {
423        DegreeRange::new(self.start().bool_not(), self.end().bool_not())
424    }
425}
426
427// Construct a range containing a single element.
428impl From<Degree> for DegreeRange {
429    fn from(degree: Degree) -> DegreeRange {
430        DegreeRange(degree, degree)
431    }
432}
433
434impl fmt::Debug for DegreeRange {
435    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
436        write!(f, "[{:?}, {:?}]", self.start(), self.end())
437    }
438}
439
440/// This type is used to track degrees of individual variables during degree
441/// propagation.
442#[derive(Default, Clone)]
443pub struct DegreeEnvironment {
444    // Even though we assume SSA a single variable may have different degrees
445    // because of parameter-dependent control flow. We track the lower and upper
446    // bounds of the degree of each variable.
447    degree_ranges: HashMap<VariableName, DegreeRange>,
448    var_types: HashMap<VariableName, VariableType>,
449}
450
451impl DegreeEnvironment {
452    pub fn new() -> DegreeEnvironment {
453        DegreeEnvironment::default()
454    }
455
456    /// Sets the degree range of the given variable. Returns true on first update.
457    /// TODO: Should probably take the supremum of the given range and any
458    /// existing range.
459    pub fn set_degree(&mut self, var: &VariableName, range: &DegreeRange) -> bool {
460        if self.degree_ranges.insert(var.clone(), range.clone()).is_none() {
461            trace!("setting degree range of `{var:?}` to {range:?}");
462            true
463        } else {
464            false
465        }
466    }
467
468    /// Sets the type of the given variable.
469    pub fn set_type(&mut self, var: &VariableName, var_type: &VariableType) {
470        if self.var_types.insert(var.clone(), var_type.clone()).is_none() {
471            trace!("setting type of `{var:?}` to `{var_type}`");
472        }
473    }
474
475    /// Gets the degree range of the given variable.
476    #[must_use]
477    pub fn degree(&self, var: &VariableName) -> Option<&DegreeRange> {
478        self.degree_ranges.get(var)
479    }
480
481    /// Returns true if the given variable is a local variable.
482    #[must_use]
483    pub fn is_local(&self, var: &VariableName) -> bool {
484        matches!(self.var_types.get(var), Some(VariableType::Local))
485    }
486}
487
488pub trait DegreeMeta {
489    /// Compute expression degrees for this node and child nodes. Returns true
490    /// if the node (or a child node) is updated.
491    fn propagate_degrees(&mut self, env: &DegreeEnvironment) -> bool;
492
493    /// Returns an inclusive range the degree of the node may take.
494    #[must_use]
495    fn degree(&self) -> Option<&DegreeRange>;
496}
497
498#[derive(Default, Clone)]
499pub struct DegreeKnowledge {
500    // The inclusive range the degree of the node may take.
501    degree_range: Option<DegreeRange>,
502}
503
504impl DegreeKnowledge {
505    #[must_use]
506    pub fn new() -> DegreeKnowledge {
507        DegreeKnowledge::default()
508    }
509
510    pub fn set_degree(&mut self, range: &DegreeRange) -> bool {
511        let result = self.degree_range.is_none();
512        self.degree_range = Some(range.clone());
513        result
514    }
515
516    #[must_use]
517    pub fn degree(&self) -> Option<&DegreeRange> {
518        self.degree_range.as_ref()
519    }
520
521    /// Returns true if the degree range is known, and the upper bound is
522    /// at most constant.
523    #[must_use]
524    pub fn is_constant(&self) -> bool {
525        if let Some(range) = &self.degree_range {
526            range.is_constant()
527        } else {
528            false
529        }
530    }
531
532    /// Returns true if the degree range is known, and the upper bound is
533    /// at most linear.
534    #[must_use]
535    pub fn is_linear(&self) -> bool {
536        if let Some(range) = &self.degree_range {
537            range.is_linear()
538        } else {
539            false
540        }
541    }
542
543    /// Returns true if the degree range is known, and the upper bound is
544    /// at most quadratic.
545    #[must_use]
546    pub fn is_quadratic(&self) -> bool {
547        if let Some(range) = &self.degree_range {
548            range.is_quadratic()
549        } else {
550            false
551        }
552    }
553}
554
555#[cfg(test)]
556mod tests {
557    use super::{Degree, DegreeKnowledge};
558
559    #[test]
560    fn test_value_knowledge() {
561        let mut value = DegreeKnowledge::new();
562        assert!(value.degree().is_none());
563        assert!(!value.is_constant());
564        assert!(!value.is_linear());
565        assert!(!value.is_quadratic());
566
567        assert!(value.set_degree(&Degree::Constant.into()));
568        assert!(value.degree().is_some());
569        assert!(value.is_constant());
570        assert!(value.is_linear());
571        assert!(value.is_quadratic());
572
573        assert!(!value.set_degree(&Degree::Linear.into()));
574        assert!(value.degree().is_some());
575        assert!(!value.is_constant());
576        assert!(value.is_linear());
577        assert!(value.is_quadratic());
578
579        assert!(!value.set_degree(&Degree::Quadratic.into()));
580        assert!(value.degree().is_some());
581        assert!(!value.is_constant());
582        assert!(!value.is_linear());
583        assert!(value.is_quadratic());
584
585        assert!(!value.set_degree(&Degree::NonQuadratic.into()));
586        assert!(value.degree().is_some());
587        assert!(!value.is_constant());
588        assert!(!value.is_linear());
589        assert!(!value.is_quadratic());
590    }
591}