sonobe_primitives/arithmetizations/mod.rs
1//! This module defines and implements traits for arithmetizations, also known
2//! as constraint systems.
3//!
4//! In Sonobe, we currently support two constraint systems: the Rank-1
5//! Constraint System (R1CS) and the Customizable Constraint System (CCS).
6//! However, user circuits are always synthesized into R1CS currently, since
7//! R1CS is the only supported constraint system by ark-relations.
8
9use ark_relations::gr1cs::SynthesisError;
10use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
11use ark_std::{fmt::Debug, log2};
12use thiserror::Error;
13
14use crate::relations::{Relation, RelationGadget};
15
16pub mod ccs;
17pub mod r1cs;
18
19/// [`enum@Error`] enumerates possible errors during arithmetization operations.
20#[derive(Error, Debug)]
21pub enum Error {
22 /// [`Error::MalformedAssignments`] indicates that the provided assignments
23 /// have incorrect shape.
24 #[error("The provided assignments have incorrect shape: {0}")]
25 MalformedAssignments(String),
26 /// [`Error::UnsatisfiedAssignments`] indicates that the provided
27 /// assignments do not satisfy the constraint system.
28 #[error("The provided assignments do not satisfy the constraint system: {0}")]
29 UnsatisfiedAssignments(String),
30 /// [`Error::InvalidNumberOfConstraints`] indicates that the constraint
31 /// system's number of constraints does not match the provided config.
32 #[error(
33 "The number of constraints in the constraint system configuration is invalid. Provided: {0}, expected: {1}"
34 )]
35 InvalidNumberOfConstraints(usize, usize),
36 /// [`Error::InvalidNumberOfVariables`] indicates that the constraint
37 /// system's number of variables does not match the provided config.
38 #[error(
39 "The number of variables in the constraint system configuration is invalid. Provided: {0}, expected: {1}"
40 )]
41 InvalidNumberOfVariables(usize, usize),
42 /// [`Error::SynthesisError`] indicates an error during constraint
43 /// synthesis.
44 #[error(transparent)]
45 SynthesisError(#[from] SynthesisError),
46}
47
48/// [`ArithConfig`] describes the configuration of a constraint system.
49#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
50pub struct ArithConfig {
51 /// [`ArithConfig::degree`] specifies the degree of the constraint system.
52 pub degree: usize,
53
54 /// [`ArithConfig::n_constraints`] specifies the number of constraints in
55 /// the constraint system.
56 pub n_constraints: usize,
57
58 /// [`ArithConfig::n_variables`] specifies the number of variables in the
59 /// constraint system.
60 pub n_variables: usize,
61
62 /// [`ArithConfig::n_public_inputs`] specifies the number of public inputs
63 /// in the constraint system.
64 pub n_public_inputs: usize,
65
66 /// [`ArithConfig::n_witnesses`] specifies the number of witnesses in the
67 /// constraint system.
68 pub n_witnesses: usize,
69}
70
71impl ArithConfig {
72 /// [`ArithConfig::log_constraints`] returns the base-2 logarithm of the
73 /// number of constraints in the constraint system.
74 pub fn log_constraints(&self) -> usize {
75 log2(self.n_constraints) as usize
76 }
77}
78
79/// [`Arith`] is a trait for constraint systems (R1CS, CCS, etc.), where we
80/// define methods to get and set configuration about the constraint system.
81/// In addition to the configuration, the implementor of this trait may also
82/// store the actual constraints and other information.
83pub trait Arith: Clone + Default + Send + Sync + CanonicalSerialize + CanonicalDeserialize {
84 /// [`Arith::config`] returns the configuration of the constraint system.
85 fn config(&self) -> ArithConfig;
86}
87
88/// [`ArithRelation`] treats a constraint system as a relation between a witness
89/// of type `W` and an instance of type `U`, and in this trait, we separate the
90/// relation check into two steps: evaluating the constraint system and checking
91/// the evaluation result.
92///
93/// Note that `W` and `U` are part of the trait parameters instead of associated
94/// types, because the same constraint system may support different types of `W`
95/// and `U`, and the satisfiability check may vary.
96/// This "same constraint system, different witness-instance pair" abstraction
97/// turns out to be very flexible, as one constraint system struct now can have
98/// many different relation checks depending on the context.
99///
100/// For example, some folding schemes consider a variant of R1CS known as
101/// relaxed R1CS, which is also represented by the `A`, `B`, and `C` matrices
102/// but has a different relation check compared to plain R1CS.
103/// We handle their similarities and differences in the following way:
104/// - Since the structure of relaxed R1CS is exactly the same as plain R1CS, we
105/// use a single R1CS struct to represent both of them.
106/// - To distinguish their relation checks, we instead use distinct types of `W`
107/// and `U`.
108/// - For plain R1CS, we use plain witness `W = w` and instance `U = x` that
109/// are simply vectors of field elements.
110/// The implementation of `ArithRelation` for such `W` and `U` then checks
111/// if `Az ∘ Bz = Cz`, where `z = [1, x, w]`.
112/// - For relaxed R1CS, we use relaxed witness `W` and relaxed instance `U`
113/// that contain extra data such as the error or slack terms, e.g.,
114/// - In Nova, `W = (w, e, ...)`, `U = (u, x, ...)`.
115/// The implementation of `ArithRelation` for such `W` and `U` checks
116/// if `Az ∘ Bz = uCz + e`, where `z = [u, x, w]`.
117/// - In ProtoGalaxy, `W = (w, ...)`, `U = (x, e, β, ...)`.
118/// The implementation of `ArithRelation` for such `W` and `U` checks
119/// if `e = Σ pow_i(β) v_i`, where `v = Az ∘ Bz - Cz`,`z = [1, x, w]`.
120///
121/// This is also the case for CCS, where `W` and `U` may be vectors of field
122/// elements or running / incoming witness-instance pairs of different folding
123/// schemes such as HyperNova.
124pub trait ArithRelation<W: ?Sized, U: ?Sized>: Arith {
125 /// [`ArithRelation::Evaluation`] defines the type of the evaluation result
126 /// returned by [`ArithRelation::eval_relation`], and consumed by
127 /// [`ArithRelation::check_evaluation`].
128 ///
129 /// The evaluation result is usually a vector of field elements.
130 /// However, we use an associated type to represent the evaluation result
131 /// for future extensions.
132 type Evaluation;
133
134 /// [`ArithRelation::eval_relation`] evaluates the constraint system at
135 /// witness `w` and instance `u`. It returns the evaluation result.
136 ///
137 /// For instance:
138 /// - Evaluating the plain R1CS at `W = w` and `U = x` returns
139 /// `Az ∘ Bz - Cz`, where `z = [1, x, w]`.
140 /// - Evaluating the relaxed R1CS in Nova at `W = (w, e, ...)` and
141 /// `U = (u, x, ...)` returns `Az ∘ Bz - uCz`, where `z = [u, x, w]`.
142 /// - Evaluating the relaxed R1CS in ProtoGalaxy at `W = (w, ...)` and
143 /// `U = (x, e, β, ...)` returns `Az ∘ Bz - Cz`, where `z = [1, x, w]`.
144 fn eval_relation(&self, w: &W, u: &U) -> Result<Self::Evaluation, Error>;
145
146 /// [`ArithRelation::check_evaluation`] checks if the evaluation result is
147 /// valid. The witness `w` and instance `u` are also parameters, because the
148 /// validity check may need information contained in `w` and/or `u`.
149 ///
150 /// For instance:
151 /// - The evaluation `v` of plain R1CS at satisfying `W` and `U` should be
152 /// an all-zero vector.
153 /// - The evaluation `v` of relaxed R1CS in Nova at satisfying `W` and `U`
154 /// should be equal to the error term `e` in `W`.
155 /// - The evaluation `v` of relaxed R1CS in ProtoGalaxy at satisfying `W`
156 /// and `U` should satisfy `e = Σ pow_i(β) v_i`, where `e` is the error
157 /// term in `U`.
158 fn check_evaluation(w: &W, u: &U, v: Self::Evaluation) -> Result<(), Error>;
159}
160
161impl<W, U, A: ArithRelation<W, U>> Relation<W, U> for A {
162 type Error = Error;
163
164 fn check_relation(&self, w: &W, u: &U) -> Result<(), Self::Error> {
165 // `check_relation` is implemented by combining `eval_relation` and
166 // `check_evaluation`.
167 let e = self.eval_relation(w, u)?;
168 Self::check_evaluation(w, u, e)
169 }
170}
171
172/// [`ArithRelationGadget`] defines the in-circuit gadget for constraint system
173/// operations in the same way as [`ArithRelation`].
174pub trait ArithRelationGadget<WVar, UVar> {
175 /// [`ArithRelationGadget::Evaluation`] defines the type of the evaluation
176 /// result returned by [`ArithRelationGadget::eval_relation`], and consumed
177 /// by [`ArithRelationGadget::check_evaluation`].
178 type Evaluation;
179
180 /// [`ArithRelationGadget::eval_relation`] evaluates the constraint system
181 /// at witness `w` and instance `u`. It returns the evaluation result.
182 fn eval_relation(&self, w: &WVar, u: &UVar) -> Result<Self::Evaluation, SynthesisError>;
183
184 /// [`ArithRelationGadget::check_evaluation`] checks if the evaluation
185 /// result is valid under the help of the witness `w` and instance `u`.
186 fn check_evaluation(w: &WVar, u: &UVar, e: Self::Evaluation) -> Result<(), SynthesisError>;
187}
188
189impl<WVar, UVar, A: ArithRelationGadget<WVar, UVar>> RelationGadget<WVar, UVar> for A {
190 fn check_relation(&self, w: &WVar, u: &UVar) -> Result<(), SynthesisError> {
191 // `check_relation` is implemented by combining `eval_relation` and
192 // `check_evaluation`.
193 let e = self.eval_relation(w, u)?;
194 Self::check_evaluation(w, u, e)
195 }
196}