use std::sync::{Arc, Mutex};
use getset::{Getters, MutGetters, Setters};
use itertools::Itertools;
use crate::{
gates::{
circuit::CircuitBuilderStage,
flex_gate::{
threads::{GateStatistics, MultiPhaseCoreManager, SinglePhaseCoreManager},
MultiPhaseThreadBreakPoints, MAX_PHASE,
},
range::RangeConfig,
RangeChip,
},
halo2_proofs::{
circuit::{Layouter, Region},
plonk::{Column, Instance},
},
utils::ScalarField,
virtual_region::{
copy_constraints::{CopyConstraintManager, SharedCopyConstraintManager},
lookups::LookupAnyManager,
manager::VirtualRegionManager,
},
AssignedValue, Context,
};
use super::BaseCircuitParams;
pub type RangeCircuitBuilder<F> = BaseCircuitBuilder<F>;
#[derive(Clone, Debug, Getters, MutGetters, Setters)]
pub struct BaseCircuitBuilder<F: ScalarField> {
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub(super) core: MultiPhaseCoreManager<F>,
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub(super) lookup_manager: [LookupAnyManager<F, 1>; MAX_PHASE],
pub config_params: BaseCircuitParams,
pub assigned_instances: Vec<Vec<AssignedValue<F>>>,
}
impl<F: ScalarField> Default for BaseCircuitBuilder<F> {
fn default() -> Self {
Self::new(false)
}
}
impl<F: ScalarField> BaseCircuitBuilder<F> {
pub fn new(witness_gen_only: bool) -> Self {
let core = MultiPhaseCoreManager::new(witness_gen_only);
let lookup_manager = [(); MAX_PHASE]
.map(|_| LookupAnyManager::new(witness_gen_only, core.copy_manager.clone()));
Self { core, lookup_manager, config_params: Default::default(), assigned_instances: vec![] }
}
pub fn from_stage(stage: CircuitBuilderStage) -> Self {
Self::new(stage.witness_gen_only()).unknown(stage == CircuitBuilderStage::Keygen)
}
pub fn prover(
config_params: BaseCircuitParams,
break_points: MultiPhaseThreadBreakPoints,
) -> Self {
Self::new(true).use_params(config_params).use_break_points(break_points)
}
pub fn set_copy_manager(&mut self, copy_manager: SharedCopyConstraintManager<F>) {
for lm in &mut self.lookup_manager {
lm.set_copy_manager(copy_manager.clone());
}
self.core.set_copy_manager(copy_manager);
}
pub fn use_copy_manager(mut self, copy_manager: SharedCopyConstraintManager<F>) -> Self {
self.set_copy_manager(copy_manager);
self
}
pub fn deep_clone(&self) -> Self {
let cm: CopyConstraintManager<F> = self.core.copy_manager.lock().unwrap().clone();
let cm_ref = Arc::new(Mutex::new(cm));
let mut clone = self.clone().use_copy_manager(cm_ref.clone());
for lm in &mut clone.lookup_manager {
*lm = lm.deep_clone(cm_ref.clone());
}
clone
}
pub fn lookup_bits(&self) -> Option<usize> {
self.config_params.lookup_bits
}
pub fn set_lookup_bits(&mut self, lookup_bits: usize) {
self.config_params.lookup_bits = Some(lookup_bits);
}
pub fn use_lookup_bits(mut self, lookup_bits: usize) -> Self {
self.set_lookup_bits(lookup_bits);
self
}
pub fn set_k(&mut self, k: usize) {
self.config_params.k = k;
}
pub fn use_k(mut self, k: usize) -> Self {
self.set_k(k);
self
}
pub fn set_instance_columns(&mut self, num_instance_columns: usize) {
self.config_params.num_instance_columns = num_instance_columns;
while self.assigned_instances.len() < num_instance_columns {
self.assigned_instances.push(vec![]);
}
assert_eq!(self.assigned_instances.len(), num_instance_columns);
}
pub fn use_instance_columns(mut self, num_instance_columns: usize) -> Self {
self.set_instance_columns(num_instance_columns);
self
}
pub fn set_params(&mut self, params: BaseCircuitParams) {
self.set_instance_columns(params.num_instance_columns);
self.config_params = params;
}
pub fn use_params(mut self, params: BaseCircuitParams) -> Self {
self.set_params(params);
self
}
pub fn break_points(&self) -> MultiPhaseThreadBreakPoints {
self.core
.phase_manager
.iter()
.map(|pm| pm.break_points.borrow().as_ref().expect("break points not set").clone())
.collect()
}
pub fn set_break_points(&mut self, break_points: MultiPhaseThreadBreakPoints) {
if break_points.is_empty() {
return;
}
self.core.touch(break_points.len() - 1);
for (pm, bp) in self.core.phase_manager.iter().zip_eq(break_points) {
*pm.break_points.borrow_mut() = Some(bp);
}
}
pub fn use_break_points(mut self, break_points: MultiPhaseThreadBreakPoints) -> Self {
self.set_break_points(break_points);
self
}
pub fn witness_gen_only(&self) -> bool {
self.core.witness_gen_only()
}
pub fn unknown(mut self, use_unknown: bool) -> Self {
self.core = self.core.unknown(use_unknown);
self
}
pub fn clear(&mut self) {
self.core.clear();
for lm in &mut self.lookup_manager {
lm.clear();
}
self.assigned_instances.iter_mut().for_each(|c| c.clear());
}
pub fn main(&mut self, phase: usize) -> &mut Context<F> {
self.core.main(phase)
}
pub fn pool(&mut self, phase: usize) -> &mut SinglePhaseCoreManager<F> {
self.core.phase_manager.get_mut(phase).unwrap()
}
pub fn new_thread(&mut self, phase: usize) -> &mut Context<F> {
self.core.new_thread(phase)
}
pub fn statistics(&self) -> RangeStatistics {
let gate = self.core.statistics();
let total_lookup_advice_per_phase = self.total_lookup_advice_per_phase();
RangeStatistics { gate, total_lookup_advice_per_phase }
}
fn total_lookup_advice_per_phase(&self) -> Vec<usize> {
self.lookup_manager.iter().map(|lm| lm.total_rows()).collect()
}
pub fn calculate_params(&mut self, minimum_rows: Option<usize>) -> BaseCircuitParams {
let k = self.config_params.k;
let ni = self.config_params.num_instance_columns;
assert_ne!(k, 0, "k must be set");
let max_rows = (1 << k) - minimum_rows.unwrap_or(0);
let gate_params = self.core.calculate_params(k, minimum_rows);
let total_lookup_advice_per_phase = self.total_lookup_advice_per_phase();
let num_lookup_advice_per_phase = total_lookup_advice_per_phase
.iter()
.map(|count| count.div_ceil(max_rows))
.collect::<Vec<_>>();
let params = BaseCircuitParams {
k: gate_params.k,
num_advice_per_phase: gate_params.num_advice_per_phase,
num_fixed: gate_params.num_fixed,
num_lookup_advice_per_phase,
lookup_bits: self.lookup_bits(),
num_instance_columns: ni,
};
self.config_params = params.clone();
#[cfg(feature = "display")]
{
println!("Total range check advice cells to lookup per phase: {total_lookup_advice_per_phase:?}");
log::debug!("Auto-calculated config params:\n {params:#?}");
}
params
}
pub fn assign_instances(
&self,
instance_columns: &[Column<Instance>],
mut layouter: impl Layouter<F>,
) {
if !self.core.witness_gen_only() {
for (instances, instance_col) in self.assigned_instances.iter().zip_eq(instance_columns)
{
for (i, instance) in instances.iter().enumerate() {
let cell = instance.cell.unwrap();
let copy_manager = self.core.copy_manager.lock().unwrap();
let cell =
copy_manager.assigned_advices.get(&cell).expect("instance not assigned");
layouter.constrain_instance(*cell, *instance_col, i);
}
}
}
}
pub fn range_chip(&self) -> RangeChip<F> {
RangeChip::new(
self.config_params.lookup_bits.expect("lookup bits not set"),
self.lookup_manager.clone(),
)
}
pub fn assign_lookups_in_phase(
&self,
config: &RangeConfig<F>,
region: &mut Region<F>,
phase: usize,
) {
let lookup_manager = self.lookup_manager.get(phase).expect("too many phases");
if lookup_manager.total_rows() == 0 {
return;
}
if let Some(q_lookup) = config.q_lookup.get(phase).and_then(|q| *q) {
assert_eq!(config.gate.basic_gates[phase].len(), 1);
if !self.witness_gen_only() {
let cells_to_lookup = lookup_manager.cells_to_lookup.lock().unwrap();
for advice in cells_to_lookup.iter().flat_map(|(_, advices)| advices) {
let cell = advice[0].cell.as_ref().unwrap();
let copy_manager = self.core.copy_manager.lock().unwrap();
let acell = copy_manager.assigned_advices[cell];
assert!(
acell.row_offset < config.gate.max_rows,
"range lookup assigned to an unusable row"
);
assert_eq!(
acell.column,
config.gate.basic_gates[phase][0].value.into(),
"lookup column does not match"
);
q_lookup.enable(region, acell.row_offset).unwrap();
}
}
} else {
let lookup_cols = config
.lookup_advice
.get(phase)
.expect("No special lookup advice columns")
.iter()
.map(|c| [*c])
.collect_vec();
assert!(!lookup_cols.is_empty(), "range lookups require lookup advice columns");
let lookup_rows = lookup_manager.total_rows();
let assigned_rows = lookup_rows.div_ceil(lookup_cols.len());
assert!(
assigned_rows <= config.gate.max_rows,
"range lookups would be assigned to unusable rows"
);
lookup_manager.assign_raw(&lookup_cols, region);
}
let _ = lookup_manager.assigned.set(());
}
}
pub struct RangeStatistics {
pub gate: GateStatistics,
pub total_lookup_advice_per_phase: Vec<usize>,
}
impl<F: ScalarField> AsRef<BaseCircuitBuilder<F>> for BaseCircuitBuilder<F> {
fn as_ref(&self) -> &BaseCircuitBuilder<F> {
self
}
}
impl<F: ScalarField> AsMut<BaseCircuitBuilder<F>> for BaseCircuitBuilder<F> {
fn as_mut(&mut self) -> &mut BaseCircuitBuilder<F> {
self
}
}