1use getset::CopyGetters;
2use itertools::Itertools;
34use crate::{
5 gates::{circuit::CircuitBuilderStage, flex_gate::FlexGateConfigParams},
6 utils::ScalarField,
7 virtual_region::copy_constraints::SharedCopyConstraintManager,
8 Context,
9};
1011use super::SinglePhaseCoreManager;
1213/// Virtual region manager for [`FlexGateConfig`](super::super::FlexGateConfig) in multiple phases.
14#[derive(Clone, Debug, Default, CopyGetters)]
15pub struct MultiPhaseCoreManager<F: ScalarField> {
16/// Virtual region for each challenge phase. These cannot be shared across threads while keeping circuit deterministic.
17pub phase_manager: Vec<SinglePhaseCoreManager<F>>,
18/// Global shared copy manager
19pub copy_manager: SharedCopyConstraintManager<F>,
20/// Flag for witness generation. If true, the gate thread builder is used for witness generation only.
21#[getset(get_copy = "pub")]
22witness_gen_only: bool,
23/// The `unknown` flag is used during key generation. If true, during key generation witness `Value`s are replaced with `Value::unknown()` for safety.
24#[getset(get_copy = "pub")]
25use_unknown: bool,
26}
2728impl<F: ScalarField> MultiPhaseCoreManager<F> {
29/// Creates a new [MultiPhaseCoreManager] with a default [SinglePhaseCoreManager] in phase 0.
30 /// Creates an empty [SharedCopyConstraintManager] and sets `witness_gen_only` flag.
31 /// * `witness_gen_only`: If true, the [MultiPhaseCoreManager] is used for witness generation only.
32 /// * If true, the gate thread builder only does witness asignments and does not store constraint information -- this should only be used for the real prover.
33 /// * If false, the gate thread builder is used for keygen and mock prover (it can also be used for real prover) and the builder stores circuit information (e.g. copy constraints, fixed columns, enabled selectors).
34 /// * These values are fixed for the circuit at key generation time, and they do not need to be re-computed by the prover in the actual proving phase.
35pub fn new(witness_gen_only: bool) -> Self {
36let copy_manager = SharedCopyConstraintManager::default();
37let phase_manager =
38vec![SinglePhaseCoreManager::new(witness_gen_only, copy_manager.clone())];
39Self { phase_manager, witness_gen_only, use_unknown: false, copy_manager }
40 }
4142/// Creates a new [MultiPhaseCoreManager] depending on the stage of circuit building. If the stage is [CircuitBuilderStage::Prover], the [MultiPhaseCoreManager] is used for witness generation only.
43pub fn from_stage(stage: CircuitBuilderStage) -> Self {
44Self::new(stage.witness_gen_only()).unknown(stage == CircuitBuilderStage::Keygen)
45 }
4647/// Mutates `self` to use the given copy manager in all phases and all threads.
48pub fn set_copy_manager(&mut self, copy_manager: SharedCopyConstraintManager<F>) {
49for pm in &mut self.phase_manager {
50 pm.set_copy_manager(copy_manager.clone());
51 }
52self.copy_manager = copy_manager;
53 }
5455/// Returns `self` with a given copy manager
56pub fn use_copy_manager(mut self, copy_manager: SharedCopyConstraintManager<F>) -> Self {
57self.set_copy_manager(copy_manager);
58self
59}
6061/// Creates a new [MultiPhaseCoreManager] with `use_unknown` flag set.
62 /// * `use_unknown`: If true, during key generation witness values are replaced with `Value::unknown()` for safety.
63pub fn unknown(mut self, use_unknown: bool) -> Self {
64self.use_unknown = use_unknown;
65for pm in &mut self.phase_manager {
66 pm.use_unknown = use_unknown;
67 }
68self
69}
7071/// Clears all threads in all phases and copy manager.
72pub fn clear(&mut self) {
73for pm in &mut self.phase_manager {
74 pm.clear();
75 }
76self.copy_manager.lock().unwrap().clear();
77 }
7879/// Returns a mutable reference to the [Context] of a gate thread. Spawns a new thread for the given phase, if none exists.
80 /// * `phase`: The challenge phase (as an index) of the gate thread.
81pub fn main(&mut self, phase: usize) -> &mut Context<F> {
82self.touch(phase);
83self.phase_manager[phase].main()
84 }
8586/// Spawns a new thread for a new given `phase`. Returns a mutable reference to the [Context] of the new thread.
87 /// * `phase`: The phase (index) of the gate thread.
88pub fn new_thread(&mut self, phase: usize) -> &mut Context<F> {
89self.touch(phase);
90self.phase_manager[phase].new_thread()
91 }
9293/// Returns a mutable reference to the [SinglePhaseCoreManager] of a given `phase`.
94pub fn in_phase(&mut self, phase: usize) -> &mut SinglePhaseCoreManager<F> {
95self.phase_manager.get_mut(phase).unwrap()
96 }
9798/// Populate `self` up to Phase `phase` (inclusive)
99pub(crate) fn touch(&mut self, phase: usize) {
100while self.phase_manager.len() <= phase {
101let _phase = self.phase_manager.len();
102let pm = SinglePhaseCoreManager::new(self.witness_gen_only, self.copy_manager.clone())
103 .in_phase(_phase);
104self.phase_manager.push(pm);
105 }
106 }
107108/// Returns some statistics about the virtual region.
109pub fn statistics(&self) -> GateStatistics {
110let total_advice_per_phase =
111self.phase_manager.iter().map(|pm| pm.total_advice()).collect::<Vec<_>>();
112113let total_fixed: usize = self
114.copy_manager
115 .lock()
116 .unwrap()
117 .constant_equalities
118 .iter()
119 .map(|(c, _)| *c)
120 .sorted()
121 .dedup()
122 .count();
123124 GateStatistics { total_advice_per_phase, total_fixed }
125 }
126127/// Auto-calculates configuration parameters for the circuit
128 ///
129 /// * `k`: The number of in the circuit (i.e. numeber of rows = 2<sup>k</sup>)
130 /// * `minimum_rows`: The minimum number of rows in the circuit that cannot be used for witness assignments and contain random `blinding factors` to ensure zk property, defaults to 0.
131pub fn calculate_params(&self, k: usize, minimum_rows: Option<usize>) -> FlexGateConfigParams {
132let max_rows = (1 << k) - minimum_rows.unwrap_or(0);
133let stats = self.statistics();
134// we do a rough estimate by taking ceil(advice_cells_per_phase / 2^k )
135 // if this is too small, manual configuration will be needed
136let num_advice_per_phase = stats
137 .total_advice_per_phase
138 .iter()
139 .map(|count| count.div_ceil(max_rows))
140 .collect::<Vec<_>>();
141let num_fixed = (stats.total_fixed + (1 << k) - 1) >> k;
142143let params = FlexGateConfigParams { num_advice_per_phase, num_fixed, k };
144#[cfg(feature = "display")]
145{
146for (phase, num_advice) in stats.total_advice_per_phase.iter().enumerate() {
147println!("Gate Chip | Phase {phase}: {num_advice} advice cells",);
148 }
149println!("Total {} fixed cells", stats.total_fixed);
150log::info!("Auto-calculated config params:\n {params:#?}");
151 }
152 params
153 }
154}
155156/// Basic statistics
157pub struct GateStatistics {
158/// Total advice cell count per phase
159pub total_advice_per_phase: Vec<usize>,
160/// Total distinct constants used
161pub total_fixed: usize,
162}