halo2_base/virtual_region/
lookups.rs1use std::collections::BTreeMap;
2use std::sync::{Arc, Mutex, OnceLock};
3
4use getset::{CopyGetters, Getters, Setters};
5
6use crate::ff::Field;
7use crate::halo2_proofs::{
8 circuit::{Region, Value},
9 plonk::{Advice, Column},
10};
11use crate::utils::halo2::{constrain_virtual_equals_external, raw_assign_advice};
12use crate::{AssignedValue, ContextTag};
13
14use super::copy_constraints::SharedCopyConstraintManager;
15use super::manager::VirtualRegionManager;
16
17pub mod basic;
19
20#[derive(Clone, Debug, Getters, CopyGetters, Setters)]
46pub struct LookupAnyManager<F: Field + Ord, const ADVICE_COLS: usize> {
47 #[allow(clippy::type_complexity)]
49 pub cells_to_lookup: Arc<Mutex<BTreeMap<ContextTag, Vec<[AssignedValue<F>; ADVICE_COLS]>>>>,
50 #[getset(get = "pub", set = "pub")]
52 copy_manager: SharedCopyConstraintManager<F>,
53 #[getset(get_copy = "pub")]
55 witness_gen_only: bool,
56 pub(crate) assigned: Arc<OnceLock<()>>,
58}
59
60impl<F: Field + Ord, const ADVICE_COLS: usize> LookupAnyManager<F, ADVICE_COLS> {
61 pub fn new(witness_gen_only: bool, copy_manager: SharedCopyConstraintManager<F>) -> Self {
63 Self {
64 witness_gen_only,
65 cells_to_lookup: Default::default(),
66 copy_manager,
67 assigned: Default::default(),
68 }
69 }
70
71 pub fn add_lookup(&self, tag: ContextTag, cells: [AssignedValue<F>; ADVICE_COLS]) {
73 self.cells_to_lookup
74 .lock()
75 .unwrap()
76 .entry(tag)
77 .and_modify(|thread| thread.push(cells))
78 .or_insert(vec![cells]);
79 }
80
81 pub fn total_rows(&self) -> usize {
83 self.cells_to_lookup.lock().unwrap().iter().flat_map(|(_, advices)| advices).count()
84 }
85
86 pub fn num_advice_chunks(&self, usable_rows: usize) -> usize {
89 let total = self.total_rows();
90 total.div_ceil(usable_rows)
91 }
92
93 pub fn clear(&mut self) {
95 self.cells_to_lookup.lock().unwrap().clear();
96 self.copy_manager.lock().unwrap().clear();
97 self.assigned = Arc::new(OnceLock::new());
98 }
99
100 pub fn deep_clone(&self, copy_manager: SharedCopyConstraintManager<F>) -> Self {
102 Self {
103 witness_gen_only: self.witness_gen_only,
104 cells_to_lookup: Arc::new(Mutex::new(self.cells_to_lookup.lock().unwrap().clone())),
105 copy_manager,
106 assigned: Default::default(),
107 }
108 }
109}
110
111impl<F: Field + Ord, const ADVICE_COLS: usize> Drop for LookupAnyManager<F, ADVICE_COLS> {
112 fn drop(&mut self) {
115 if Arc::strong_count(&self.cells_to_lookup) > 1 {
116 return;
117 }
118 if self.total_rows() > 0 && self.assigned.get().is_none() {
119 dbg!("WARNING: LookupAnyManager was not assigned!");
120 }
121 }
122}
123
124impl<F: Field + Ord, const ADVICE_COLS: usize> VirtualRegionManager<F>
125 for LookupAnyManager<F, ADVICE_COLS>
126{
127 type Config = Vec<[Column<Advice>; ADVICE_COLS]>;
128 type Assignment = ();
129
130 fn assign_raw(&self, config: &Self::Config, region: &mut Region<F>) {
131 let mut copy_manager =
132 (!self.witness_gen_only).then(|| self.copy_manager().lock().unwrap());
133 let cells_to_lookup = self.cells_to_lookup.lock().unwrap();
134 let mut lookup_offset = 0;
137 let mut lookup_col = 0;
138 for advices in cells_to_lookup.iter().flat_map(|(_, advices)| advices) {
139 if lookup_col >= config.len() {
140 lookup_col = 0;
141 lookup_offset += 1;
142 }
143 for (advice, &column) in advices.iter().zip(config[lookup_col].iter()) {
144 let bcell =
145 raw_assign_advice(region, column, lookup_offset, Value::known(advice.value));
146 if let Some(copy_manager) = copy_manager.as_mut() {
147 constrain_virtual_equals_external(region, *advice, bcell.cell(), copy_manager);
148 }
149 }
150
151 lookup_col += 1;
152 }
153 let _ = self.assigned.set(());
155 }
156}