halo2_base/gates/flex_gate/threads/
multi_phase.rs

1use getset::CopyGetters;
2use itertools::Itertools;
3
4use crate::{
5    gates::{circuit::CircuitBuilderStage, flex_gate::FlexGateConfigParams},
6    utils::ScalarField,
7    virtual_region::copy_constraints::SharedCopyConstraintManager,
8    Context,
9};
10
11use super::SinglePhaseCoreManager;
12
13/// 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.
17    pub phase_manager: Vec<SinglePhaseCoreManager<F>>,
18    /// Global shared copy manager
19    pub 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")]
22    witness_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")]
25    use_unknown: bool,
26}
27
28impl<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.
35    pub fn new(witness_gen_only: bool) -> Self {
36        let copy_manager = SharedCopyConstraintManager::default();
37        let phase_manager =
38            vec![SinglePhaseCoreManager::new(witness_gen_only, copy_manager.clone())];
39        Self { phase_manager, witness_gen_only, use_unknown: false, copy_manager }
40    }
41
42    /// 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.
43    pub fn from_stage(stage: CircuitBuilderStage) -> Self {
44        Self::new(stage.witness_gen_only()).unknown(stage == CircuitBuilderStage::Keygen)
45    }
46
47    /// Mutates `self` to use the given copy manager in all phases and all threads.
48    pub fn set_copy_manager(&mut self, copy_manager: SharedCopyConstraintManager<F>) {
49        for pm in &mut self.phase_manager {
50            pm.set_copy_manager(copy_manager.clone());
51        }
52        self.copy_manager = copy_manager;
53    }
54
55    /// Returns `self` with a given copy manager
56    pub fn use_copy_manager(mut self, copy_manager: SharedCopyConstraintManager<F>) -> Self {
57        self.set_copy_manager(copy_manager);
58        self
59    }
60
61    /// 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.
63    pub fn unknown(mut self, use_unknown: bool) -> Self {
64        self.use_unknown = use_unknown;
65        for pm in &mut self.phase_manager {
66            pm.use_unknown = use_unknown;
67        }
68        self
69    }
70
71    /// Clears all threads in all phases and copy manager.
72    pub fn clear(&mut self) {
73        for pm in &mut self.phase_manager {
74            pm.clear();
75        }
76        self.copy_manager.lock().unwrap().clear();
77    }
78
79    /// 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.
81    pub fn main(&mut self, phase: usize) -> &mut Context<F> {
82        self.touch(phase);
83        self.phase_manager[phase].main()
84    }
85
86    /// 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.
88    pub fn new_thread(&mut self, phase: usize) -> &mut Context<F> {
89        self.touch(phase);
90        self.phase_manager[phase].new_thread()
91    }
92
93    /// Returns a mutable reference to the [SinglePhaseCoreManager] of a given `phase`.
94    pub fn in_phase(&mut self, phase: usize) -> &mut SinglePhaseCoreManager<F> {
95        self.phase_manager.get_mut(phase).unwrap()
96    }
97
98    /// Populate `self` up to Phase `phase` (inclusive)
99    pub(crate) fn touch(&mut self, phase: usize) {
100        while self.phase_manager.len() <= phase {
101            let _phase = self.phase_manager.len();
102            let pm = SinglePhaseCoreManager::new(self.witness_gen_only, self.copy_manager.clone())
103                .in_phase(_phase);
104            self.phase_manager.push(pm);
105        }
106    }
107
108    /// Returns some statistics about the virtual region.
109    pub fn statistics(&self) -> GateStatistics {
110        let total_advice_per_phase =
111            self.phase_manager.iter().map(|pm| pm.total_advice()).collect::<Vec<_>>();
112
113        let 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();
123
124        GateStatistics { total_advice_per_phase, total_fixed }
125    }
126
127    /// 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.
131    pub fn calculate_params(&self, k: usize, minimum_rows: Option<usize>) -> FlexGateConfigParams {
132        let max_rows = (1 << k) - minimum_rows.unwrap_or(0);
133        let 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
136        let num_advice_per_phase = stats
137            .total_advice_per_phase
138            .iter()
139            .map(|count| count.div_ceil(max_rows))
140            .collect::<Vec<_>>();
141        let num_fixed = (stats.total_fixed + (1 << k) - 1) >> k;
142
143        let params = FlexGateConfigParams { num_advice_per_phase, num_fixed, k };
144        #[cfg(feature = "display")]
145        {
146            for (phase, num_advice) in stats.total_advice_per_phase.iter().enumerate() {
147                println!("Gate Chip | Phase {phase}: {num_advice} advice cells",);
148            }
149            println!("Total {} fixed cells", stats.total_fixed);
150            log::info!("Auto-calculated config params:\n {params:#?}");
151        }
152        params
153    }
154}
155
156/// Basic statistics
157pub struct GateStatistics {
158    /// Total advice cell count per phase
159    pub total_advice_per_phase: Vec<usize>,
160    /// Total distinct constants used
161    pub total_fixed: usize,
162}