1use itertools::Itertools;
2use openvm_native_compiler::{
3 ir::{Builder, Config},
4 prelude::*,
5};
6use openvm_stark_backend::{
7 keygen::types::TraceWidth,
8 p3_field::{FieldAlgebra, PrimeField32},
9 p3_util::log2_strict_usize,
10};
11
12use crate::{
13 types::{MultiStarkVerificationAdvice, StarkVerificationAdvice},
14 vars::{
15 LinearConstraintVariable, MultiStarkVerificationAdviceVariable, OptionalVar,
16 StarkVerificationAdviceVariable, TraceHeightConstraintSystem, TraceWidthVariable,
17 },
18};
19
20pub fn get_advice_per_air<C: Config>(
21 builder: &mut Builder<C>,
22 m_advice: &MultiStarkVerificationAdvice<C>,
23 air_ids: &Array<C, Usize<C::N>>,
24) -> MultiStarkVerificationAdviceVariable<C> {
25 let num_challenges_to_sample_mask = m_advice
26 .num_challenges_to_sample
27 .iter()
28 .map(|&num_challenges_to_sample| vec![builder.eval(RVar::zero()); num_challenges_to_sample])
29 .collect_vec();
30 let advice_per_air = builder.array(air_ids.len());
31
32 let idx: Usize<_> = builder.eval(RVar::zero());
33 for (air_id, advice) in m_advice.per_air.iter().enumerate() {
34 builder.if_ne(idx.clone(), air_ids.len()).then(|builder| {
35 let curr_air_id = builder.get(air_ids, idx.clone());
36 let air_id = Usize::from(air_id);
37 builder.if_eq(air_id, curr_air_id).then(|builder| {
38 let advice_var = constant_advice_and_update_mask(
39 builder,
40 advice,
41 &num_challenges_to_sample_mask,
42 );
43 builder.set_value(&advice_per_air, idx.clone(), advice_var);
44 builder.inc(&idx);
45 });
46 });
47 }
48 builder.assert_var_eq(idx, air_ids.len());
53
54 let trace_height_constraints = m_advice
55 .trace_height_constraints
56 .iter()
57 .map(|constraint| {
58 let coefficients = builder.array(constraint.coefficients.len());
59 for (i, coeff) in constraint.coefficients.iter().enumerate() {
60 let coefficient: Var<_> = builder.constant(C::N::from_canonical_u32(*coeff));
61 builder.set(&coefficients, i, coefficient);
62 }
63 assert!(constraint.threshold <= C::F::ORDER_U32);
64 let threshold: Var<_> = builder.constant(C::N::from_wrapped_u32(constraint.threshold));
65 let is_threshold_at_p = constraint.threshold == C::F::ORDER_U32;
66 LinearConstraintVariable {
67 coefficients,
68 threshold,
69 is_threshold_at_p,
70 }
71 })
72 .collect();
73
74 let height_maxes = builder.array(m_advice.per_air.len());
75 for i in 0..m_advice.per_air.len() {
76 let max_coefficient = m_advice
77 .trace_height_constraints
78 .iter()
79 .map(|constraint| constraint.coefficients[i])
80 .max()
81 .unwrap();
82 let height_max = if max_coefficient <= 1 {
83 OptionalVar {
84 is_some: Usize::from(0),
85 value: builder.constant(C::N::ZERO),
86 }
87 } else {
88 OptionalVar {
89 is_some: Usize::from(1),
90 value: builder.constant(C::N::from_canonical_u32(
93 C::F::ORDER_U32 / max_coefficient + 1,
94 )),
95 }
96 };
97 builder.set(&height_maxes, i, height_max);
98 }
99
100 MultiStarkVerificationAdviceVariable {
101 per_air: advice_per_air,
102 num_challenges_to_sample_mask,
103 trace_height_constraint_system: TraceHeightConstraintSystem {
104 height_constraints: trace_height_constraints,
105 height_maxes,
106 },
107 }
108}
109
110fn constant_advice_and_update_mask<C: Config>(
111 builder: &mut Builder<C>,
112 advice: &StarkVerificationAdvice<C>,
113 num_challenges_to_sample_mask: &[Vec<Usize<C::N>>],
114) -> StarkVerificationAdviceVariable<C> {
115 let preprocessed_data = if let Some(preprocessed_data) = advice.preprocessed_data.as_ref() {
116 let commit = builder.constant(preprocessed_data.commit.clone());
117 let arr = builder.array(1);
118 builder.set_value(&arr, 0, commit);
119 arr
120 } else {
121 builder.array(0)
122 };
123 for (phase, &num_challenges) in advice.num_challenges_to_sample.iter().enumerate() {
124 for i in 0..num_challenges {
125 builder.assign(&num_challenges_to_sample_mask[phase][i], RVar::one());
126 }
127 }
128 StarkVerificationAdviceVariable {
129 preprocessed_data,
130 width: constant_trace_width(builder, &advice.width),
131 log_quotient_degree: builder.eval(RVar::from(log2_strict_usize(advice.quotient_degree))),
132 num_public_values: builder.eval(RVar::from(advice.num_public_values)),
133 num_challenges_to_sample: constant_usize_array(builder, &advice.num_challenges_to_sample),
134 num_exposed_values_after_challenge: constant_usize_array(
135 builder,
136 &advice.num_exposed_values_after_challenge,
137 ),
138 }
139}
140
141fn constant_trace_width<C: Config>(
142 builder: &mut Builder<C>,
143 trace_width: &TraceWidth,
144) -> TraceWidthVariable<C> {
145 TraceWidthVariable {
146 preprocessed: constant_usize_option(builder, &trace_width.preprocessed),
147 cached_mains: constant_usize_array(builder, &trace_width.cached_mains),
148 common_main: builder.eval(RVar::from(trace_width.common_main)),
149 after_challenge: constant_usize_array(builder, &trace_width.after_challenge),
150 }
151}
152
153fn constant_usize_option<C: Config>(
154 builder: &mut Builder<C>,
155 opt: &Option<usize>,
156) -> Array<C, Usize<C::N>> {
157 match opt {
158 Some(val) => {
159 let arr = builder.array(1);
160 let v: Usize<_> = builder.eval(RVar::from(*val));
161 builder.set_value(&arr, 0, v);
162 arr
163 }
164 None => builder.array(0),
165 }
166}
167
168fn constant_usize_array<C: Config>(
169 builder: &mut Builder<C>,
170 arr: &[usize],
171) -> Array<C, Usize<C::N>> {
172 let carr = builder.array(arr.len());
173 for (i, val) in arr.iter().enumerate() {
174 let v: Usize<_> = builder.eval(RVar::from(*val));
175 builder.set(&carr, i, v);
176 }
177 carr
178}