Skip to content

Commit a97c666

Browse files
feat: serialize SymbolicConstraints as DAG (#14)
* chore: clarify comment * feat: serialize `SymbolicConstraints` as DAG Including interactions. Doing this for now to keep interfaces the same. In the upcoming frontend-backend split, we will make a breaking interface change to switch to using the DAG as the primary interface and make `SymbolicExpression` and `SymbolicConstraints` private (or remove entirely). * chore: bump version to v0.1.2
1 parent 42bcab0 commit a97c666

File tree

5 files changed

+153
-61
lines changed

5 files changed

+153
-61
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace.package]
2-
version = "0.1.0-alpha"
2+
version = "0.1.2-alpha"
33
edition = "2021"
44
rust-version = "1.82"
55
authors = ["OpenVM contributors"]

crates/stark-backend/src/air_builders/symbolic/dag.rs

+133-26
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ use p3_field::Field;
44
use rustc_hash::FxHashMap;
55
use serde::{Deserialize, Serialize};
66

7-
use crate::air_builders::symbolic::{
8-
symbolic_expression::SymbolicExpression, symbolic_variable::SymbolicVariable,
7+
use super::SymbolicConstraints;
8+
use crate::{
9+
air_builders::symbolic::{
10+
symbolic_expression::SymbolicExpression, symbolic_variable::SymbolicVariable,
11+
},
12+
interaction::{Interaction, SymbolicInteraction},
913
};
1014

1115
/// A node in symbolic expression DAG.
@@ -50,18 +54,62 @@ pub struct SymbolicExpressionDag<F> {
5054
pub(crate) constraint_idx: Vec<usize>,
5155
}
5256

53-
pub(crate) fn build_symbolic_expr_dag<F: Field>(
54-
exprs: &[SymbolicExpression<F>],
55-
) -> SymbolicExpressionDag<F> {
57+
#[derive(Clone, Debug, Serialize, Deserialize)]
58+
#[serde(bound = "F: Field")]
59+
pub struct SymbolicConstraintsDag<F> {
60+
/// DAG with all symbolic expressions as nodes.
61+
/// A subset of the nodes represents all constraints that will be
62+
/// included in the quotient polynomial via DEEP-ALI.
63+
pub constraints: SymbolicExpressionDag<F>,
64+
/// List of all interactions, where expressions in the interactions
65+
/// are referenced by node idx as `usize`.
66+
///
67+
/// This is used by the prover for after challenge trace generation,
68+
/// and some partial information may be used by the verifier.
69+
///
70+
/// **However**, any contributions to the quotient polynomial from
71+
/// logup are already included in `constraints` and do not need to
72+
/// be separately calculated from `interactions`.
73+
pub interactions: Vec<Interaction<usize>>,
74+
}
75+
76+
pub(crate) fn build_symbolic_constraints_dag<F: Field>(
77+
constraints: &[SymbolicExpression<F>],
78+
interactions: &[SymbolicInteraction<F>],
79+
) -> SymbolicConstraintsDag<F> {
5680
let mut expr_to_idx = FxHashMap::default();
5781
let mut nodes = Vec::new();
58-
let constraint_idx = exprs
82+
let constraint_idx = constraints
5983
.iter()
6084
.map(|expr| topological_sort_symbolic_expr(expr, &mut expr_to_idx, &mut nodes))
6185
.collect();
62-
SymbolicExpressionDag {
86+
let interactions: Vec<Interaction<usize>> = interactions
87+
.iter()
88+
.map(|interaction| {
89+
let fields: Vec<usize> = interaction
90+
.fields
91+
.iter()
92+
.map(|field_expr| {
93+
topological_sort_symbolic_expr(field_expr, &mut expr_to_idx, &mut nodes)
94+
})
95+
.collect();
96+
let count =
97+
topological_sort_symbolic_expr(&interaction.count, &mut expr_to_idx, &mut nodes);
98+
Interaction {
99+
fields,
100+
count,
101+
bus_index: interaction.bus_index,
102+
interaction_type: interaction.interaction_type,
103+
}
104+
})
105+
.collect();
106+
let constraints = SymbolicExpressionDag {
63107
nodes,
64108
constraint_idx,
109+
};
110+
SymbolicConstraintsDag {
111+
constraints,
112+
interactions,
65113
}
66114
}
67115

@@ -139,8 +187,9 @@ fn topological_sort_symbolic_expr<'a, F: Field>(
139187
}
140188

141189
impl<F: Field> SymbolicExpressionDag<F> {
142-
/// Returns symbolic expressions for each constraint
143-
pub fn to_symbolic_expressions(&self) -> Vec<SymbolicExpression<F>> {
190+
/// Convert each node to a [`SymbolicExpression<F>`] reference and return
191+
/// the full list.
192+
fn to_symbolic_expressions(&self) -> Vec<Arc<SymbolicExpression<F>>> {
144193
let mut exprs: Vec<Arc<SymbolicExpression<_>>> = Vec::with_capacity(self.nodes.len());
145194
for node in &self.nodes {
146195
let expr = match *node {
@@ -186,10 +235,48 @@ impl<F: Field> SymbolicExpressionDag<F> {
186235
};
187236
exprs.push(Arc::new(expr));
188237
}
189-
self.constraint_idx
190-
.iter()
191-
.map(|&idx| exprs[idx].as_ref().clone())
192-
.collect()
238+
exprs
239+
}
240+
}
241+
242+
// TEMPORARY conversions until we switch main interfaces to use SymbolicConstraintsDag
243+
impl<F: Field> From<SymbolicConstraintsDag<F>> for SymbolicConstraints<F> {
244+
fn from(dag: SymbolicConstraintsDag<F>) -> Self {
245+
let exprs = dag.constraints.to_symbolic_expressions();
246+
let constraints = dag
247+
.constraints
248+
.constraint_idx
249+
.into_iter()
250+
.map(|idx| exprs[idx].as_ref().clone())
251+
.collect::<Vec<_>>();
252+
let interactions = dag
253+
.interactions
254+
.into_iter()
255+
.map(|interaction| {
256+
let fields = interaction
257+
.fields
258+
.into_iter()
259+
.map(|idx| exprs[idx].as_ref().clone())
260+
.collect();
261+
let count = exprs[interaction.count].as_ref().clone();
262+
Interaction {
263+
fields,
264+
count,
265+
bus_index: interaction.bus_index,
266+
interaction_type: interaction.interaction_type,
267+
}
268+
})
269+
.collect::<Vec<_>>();
270+
SymbolicConstraints {
271+
constraints,
272+
interactions,
273+
}
274+
}
275+
}
276+
277+
impl<F: Field> From<SymbolicConstraints<F>> for SymbolicConstraintsDag<F> {
278+
fn from(sc: SymbolicConstraints<F>) -> Self {
279+
build_symbolic_constraints_dag(&sc.constraints, &sc.interactions)
193280
}
194281
}
195282

@@ -198,17 +285,20 @@ mod tests {
198285
use p3_baby_bear::BabyBear;
199286
use p3_field::AbstractField;
200287

201-
use crate::air_builders::symbolic::{
202-
dag::{build_symbolic_expr_dag, SymbolicExpressionDag, SymbolicExpressionNode},
203-
symbolic_expression::SymbolicExpression,
204-
symbolic_variable::{Entry, SymbolicVariable},
205-
SymbolicConstraints,
288+
use crate::{
289+
air_builders::symbolic::{
290+
dag::{build_symbolic_constraints_dag, SymbolicExpressionDag, SymbolicExpressionNode},
291+
symbolic_expression::SymbolicExpression,
292+
symbolic_variable::{Entry, SymbolicVariable},
293+
SymbolicConstraints,
294+
},
295+
interaction::{Interaction, InteractionType},
206296
};
207297

208298
type F = BabyBear;
209299

210300
#[test]
211-
fn test_symbolic_expressions_dag() {
301+
fn test_symbolic_constraints_dag() {
212302
let expr = SymbolicExpression::Constant(F::ONE)
213303
* SymbolicVariable::new(
214304
Entry::Main {
@@ -217,16 +307,22 @@ mod tests {
217307
},
218308
3,
219309
);
220-
let exprs = vec![
310+
let constraints = vec![
221311
SymbolicExpression::IsFirstRow * SymbolicExpression::IsLastRow
222312
+ SymbolicExpression::Constant(F::ONE)
223313
+ SymbolicExpression::IsFirstRow * SymbolicExpression::IsLastRow
224314
+ expr.clone(),
225315
expr.clone() * expr.clone(),
226316
];
227-
let expr_list = build_symbolic_expr_dag(&exprs);
317+
let interactions = vec![Interaction {
318+
bus_index: 0,
319+
fields: vec![expr.clone(), SymbolicExpression::Constant(F::TWO)],
320+
count: SymbolicExpression::Constant(F::ONE),
321+
interaction_type: InteractionType::Send,
322+
}];
323+
let dag = build_symbolic_constraints_dag(&constraints, &interactions);
228324
assert_eq!(
229-
expr_list,
325+
dag.constraints,
230326
SymbolicExpressionDag::<F> {
231327
nodes: vec![
232328
SymbolicExpressionNode::IsFirstRow,
@@ -274,17 +370,28 @@ mod tests {
274370
left_idx: 8,
275371
right_idx: 8,
276372
degree_multiple: 2
277-
}
373+
},
374+
SymbolicExpressionNode::Constant(F::TWO),
278375
],
279376
constraint_idx: vec![9, 10],
280377
}
281378
);
379+
assert_eq!(
380+
dag.interactions,
381+
vec![Interaction {
382+
bus_index: 0,
383+
fields: vec![8, 11],
384+
count: 3,
385+
interaction_type: InteractionType::Send,
386+
}]
387+
);
388+
282389
let sc = SymbolicConstraints {
283-
constraints: exprs,
284-
interactions: vec![],
390+
constraints,
391+
interactions,
285392
};
286393
let ser_str = serde_json::to_string(&sc).unwrap();
287394
let new_sc: SymbolicConstraints<_> = serde_json::from_str(&ser_str).unwrap();
288-
assert_eq!(sc.constraints, new_sc.constraints);
395+
assert_eq!(sc, new_sc);
289396
}
290397
}

crates/stark-backend/src/air_builders/symbolic/mod.rs

+15-30
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use p3_air::{
77
use p3_field::Field;
88
use p3_matrix::{dense::RowMajorMatrix, Matrix};
99
use p3_util::log2_ceil_usize;
10-
use serde::{Deserialize, Deserializer, Serialize, Serializer};
10+
use serde::{Deserialize, Serialize};
1111
use tracing::instrument;
1212

1313
use self::{
@@ -16,7 +16,6 @@ use self::{
1616
};
1717
use super::PartitionedAirBuilder;
1818
use crate::{
19-
air_builders::symbolic::dag::{build_symbolic_expr_dag, SymbolicExpressionDag},
2019
interaction::{
2120
rap::InteractionPhaseAirBuilder, Interaction, InteractionBuilder, InteractionType,
2221
RapPhaseSeqKind, SymbolicInteraction,
@@ -31,13 +30,23 @@ pub mod symbolic_variable;
3130

3231
/// Symbolic constraints for a single AIR with interactions.
3332
/// The constraints contain the constraints on the logup partial sums.
34-
#[derive(Clone, Debug, Serialize, Deserialize)]
35-
#[serde(bound = "F: Field")]
33+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
34+
#[serde(
35+
bound = "F: Field",
36+
from = "dag::SymbolicConstraintsDag<F>",
37+
into = "dag::SymbolicConstraintsDag<F>"
38+
)]
3639
pub struct SymbolicConstraints<F> {
3740
/// All constraints of the RAP, including the constraints on the logup partial sums.
3841
pub constraints: Vec<SymbolicExpression<F>>,
39-
/// Only for debug purposes. `constraints` also contains the constraints on the logup partial sums.
40-
pub interactions: Vec<Interaction<SymbolicExpression<F>>>,
42+
/// Symbolic representation of chip interactions. This is used by
43+
/// the prover for after challenge trace generation, and some partial
44+
/// information may be used by the verifier.
45+
///
46+
/// **However**, any contributions to the quotient polynomial from
47+
/// logup are already included in `constraints` and do not need to
48+
/// be separately calculated from `interactions`.
49+
pub interactions: Vec<SymbolicInteraction<F>>,
4150
}
4251

4352
impl<F: Field> SymbolicConstraints<F> {
@@ -467,27 +476,3 @@ fn gen_main_trace<F: Field>(
467476
.collect_vec();
468477
RowMajorMatrix::new(mat_values, width)
469478
}
470-
471-
#[allow(dead_code)]
472-
fn serialize_symbolic_exprs<F: Field, S>(
473-
data: &[SymbolicExpression<F>],
474-
serializer: S,
475-
) -> Result<S::Ok, S::Error>
476-
where
477-
S: Serializer,
478-
{
479-
// Convert the number to a hex string before serializing
480-
let dag = build_symbolic_expr_dag(data);
481-
dag.serialize(serializer)
482-
}
483-
484-
#[allow(dead_code)]
485-
fn deserialize_symbolic_exprs<'de, F: Field, D>(
486-
deserializer: D,
487-
) -> Result<Vec<SymbolicExpression<F>>, D::Error>
488-
where
489-
D: Deserializer<'de>,
490-
{
491-
let dag = SymbolicExpressionDag::deserialize(deserializer)?;
492-
Ok(dag.to_symbolic_expressions())
493-
}

crates/stark-backend/src/air_builders/verifier.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use p3_matrix::Matrix;
88

99
use super::{
1010
symbolic::{
11-
dag::{build_symbolic_expr_dag, SymbolicExpressionNode},
11+
dag::{build_symbolic_constraints_dag, SymbolicExpressionNode},
1212
symbolic_expression::{SymbolicEvaluator, SymbolicExpression},
1313
symbolic_variable::{Entry, SymbolicVariable},
1414
},
@@ -52,7 +52,7 @@ where
5252
PubVar: Into<Expr> + Copy + Send + Sync,
5353
{
5454
pub fn eval_constraints(&mut self, constraints: &[SymbolicExpression<F>]) {
55-
let dag = build_symbolic_expr_dag(constraints);
55+
let dag = build_symbolic_constraints_dag(constraints, &[]).constraints;
5656
// node_idx -> evaluation
5757
// We do a simple serial evaluation in topological order.
5858
// This can be parallelized if necessary.

crates/stark-backend/src/interaction/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ pub mod stark_log_up;
1818
pub mod trace;
1919
mod utils;
2020

21-
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
21+
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2222
pub enum InteractionType {
2323
Send,
2424
Receive,
2525
}
2626

27-
#[derive(Clone, Debug, Serialize, Deserialize)]
27+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2828
pub struct Interaction<Expr> {
2929
pub fields: Vec<Expr>,
3030
pub count: Expr,

0 commit comments

Comments
 (0)