1use std::{array, backtrace::Backtrace, collections::HashMap, mem, ops::Deref, sync::Arc};
5
6use binius_field::Field;
7use bytemuck::zeroed_vec;
8use smallvec::{SmallVec, smallvec};
9
10use crate::constraint_system::{
11 ConstraintSystem, ConstraintWire, MulConstraint, Operand, WireKind, Witness, WitnessIndex,
12 WitnessLayout, WitnessSegment,
13};
14
15pub trait CircuitBuilder {
21 type Wire: Copy;
22 type Field: Field;
23
24 fn assert_zero(&mut self, wire: Self::Wire);
25
26 fn assert_eq(&mut self, lhs: Self::Wire, rhs: Self::Wire) {
27 let diff = self.add(lhs, rhs);
28 self.assert_zero(diff);
29 }
30
31 fn constant(&mut self, val: Self::Field) -> Self::Wire;
32
33 fn add(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire;
34
35 fn sub(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire {
37 self.add(lhs, rhs)
38 }
39
40 fn mul(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire;
41
42 fn hint<H: Fn([Self::Field; IN]) -> [Self::Field; OUT], const IN: usize, const OUT: usize>(
43 &mut self,
44 inputs: [Self::Wire; IN],
45 f: H,
46 ) -> [Self::Wire; OUT];
47
48 fn hint_varsize<H: FnOnce(&[Self::Field]) -> Vec<Self::Field>>(
54 &mut self,
55 inputs: &[Self::Wire],
56 out_len: usize,
57 f: H,
58 ) -> Vec<Self::Wire>;
59}
60
61#[derive(Debug)]
62pub struct WireAllocator {
63 n_wires: u32,
64 kind: WireKind,
65}
66
67impl WireAllocator {
68 pub const fn new(kind: WireKind) -> Self {
69 WireAllocator { n_wires: 0, kind }
70 }
71
72 pub const fn alloc(&mut self) -> ConstraintWire {
73 let wire = ConstraintWire {
74 kind: self.kind,
75 id: self.n_wires,
76 };
77 self.n_wires += 1;
78 wire
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum WireStatus {
90 Unknown,
92 Pinned,
94 Pruned,
96}
97
98#[derive(Debug)]
104pub struct ConstraintSystemIR<F: Field> {
105 pub(crate) constant_alloc: WireAllocator,
106 pub(crate) public_alloc: WireAllocator,
107 pub(crate) derived_alloc: WireAllocator,
108 pub(crate) precommit_alloc: WireAllocator,
109 pub(crate) private_alloc: WireAllocator,
110 pub(crate) constants: HashMap<F, u32>,
111 pub(crate) zero_constraints: Vec<Operand<ConstraintWire>>,
112 pub(crate) mul_constraints: Vec<MulConstraint<ConstraintWire>>,
113 pub(crate) private_wires_status: Vec<WireStatus>,
116}
117
118impl<F: Field> ConstraintSystemIR<F> {
119 pub fn finalize(mut self) -> (ConstraintSystem<F>, WitnessLayout<F>) {
126 let one_id = self
128 .constants
129 .entry(F::ONE)
130 .or_insert_with(|| self.constant_alloc.alloc().id);
131 let one_wire = ConstraintWire {
132 kind: WireKind::Constant,
133 id: *one_id,
134 };
135
136 let zero_const_id: Option<u32> = self.constants.get(&F::ZERO).copied();
138
139 let mut constants = zeroed_vec(self.constant_alloc.n_wires as usize);
141 for (val, id) in self.constants {
142 constants[id as usize] = val;
143 }
144
145 let one_operand = Operand::from(one_wire);
147 let zero_operand = Operand::default();
148 for operand in mem::take(&mut self.zero_constraints) {
149 if !operand.is_empty() {
150 self.mul_constraints.push(MulConstraint {
151 a: operand,
152 b: one_operand.clone(),
153 c: zero_operand.clone(),
154 });
155 }
156 }
157
158 let private_alive: Vec<bool> = self
160 .private_wires_status
161 .iter()
162 .map(|&status| !matches!(status, WireStatus::Pruned))
163 .collect();
164
165 let mut derived_alive = vec![false; self.derived_alloc.n_wires as usize];
170 for MulConstraint { a, b, c } in &self.mul_constraints {
171 for operand in [a, b, c] {
172 for wire in operand.wires() {
173 if wire.kind == WireKind::Derived {
174 derived_alive[wire.id as usize] = true;
175 }
176 }
177 }
178 }
179
180 let layout = WitnessLayout::sparse(
182 constants.clone(),
183 self.public_alloc.n_wires,
184 self.precommit_alloc.n_wires,
185 &derived_alive,
186 &private_alive,
187 );
188
189 let map_operand = |operand: &Operand<ConstraintWire>| -> Operand<WitnessIndex> {
191 let indices: SmallVec<[WitnessIndex; 4]> = operand
192 .wires()
193 .iter()
194 .filter(|wire| !(wire.kind == WireKind::Constant && zero_const_id == Some(wire.id)))
195 .filter_map(|wire| layout.get(wire))
196 .collect();
197 Operand::new(indices)
198 };
199
200 let mul_constraints = self
201 .mul_constraints
202 .iter()
203 .map(|constraint| MulConstraint {
204 a: map_operand(&constraint.a),
205 b: map_operand(&constraint.b),
206 c: map_operand(&constraint.c),
207 })
208 .collect();
209
210 let one_wire_index = layout
212 .get(&one_wire)
213 .expect("one_wire constant should exist in layout")
214 .index;
215
216 let cs = ConstraintSystem::new(
217 constants,
218 layout.n_inout() as u32,
219 layout.n_precommit() as u32,
220 layout.n_private() as u32,
221 layout.log_public(),
222 mul_constraints,
223 one_wire_index,
224 );
225
226 (cs, layout)
227 }
228}
229
230#[derive(Debug)]
235pub struct ConstraintBuilder<F: Field> {
236 ir: ConstraintSystemIR<F>,
237}
238
239impl<F: Field> ConstraintBuilder<F> {
240 #[allow(clippy::new_without_default)]
241 pub fn new() -> Self {
242 ConstraintBuilder {
243 ir: ConstraintSystemIR {
244 constant_alloc: WireAllocator::new(WireKind::Constant),
245 public_alloc: WireAllocator::new(WireKind::InOut),
246 derived_alloc: WireAllocator::new(WireKind::Derived),
247 precommit_alloc: WireAllocator::new(WireKind::Precommit),
248 private_alloc: WireAllocator::new(WireKind::Private),
249 constants: HashMap::new(),
250 zero_constraints: Vec::new(),
251 mul_constraints: Vec::new(),
252 private_wires_status: Vec::new(),
253 },
254 }
255 }
256
257 pub const fn alloc_inout(&mut self) -> ConstraintWire {
258 self.ir.public_alloc.alloc()
259 }
260
261 pub const fn alloc_precommit(&mut self) -> ConstraintWire {
262 self.ir.precommit_alloc.alloc()
263 }
264
265 pub fn build(self) -> ConstraintSystemIR<F> {
266 self.ir
267 }
268}
269
270impl<F: Field> CircuitBuilder for ConstraintBuilder<F> {
271 type Wire = ConstraintWire;
272 type Field = F;
273
274 fn assert_zero(&mut self, wire: Self::Wire) {
275 self.ir.zero_constraints.push(wire.into());
276 }
277
278 fn assert_eq(&mut self, lhs: Self::Wire, rhs: Self::Wire) {
279 self.ir
280 .zero_constraints
281 .push(Operand::new(smallvec![lhs, rhs]));
282 }
283
284 fn constant(&mut self, val: F) -> Self::Wire {
285 let id = self
286 .ir
287 .constants
288 .entry(val)
289 .or_insert_with(|| self.ir.constant_alloc.alloc().id);
290 ConstraintWire {
291 kind: WireKind::Constant,
292 id: *id,
293 }
294 }
295
296 fn add(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire {
297 if lhs.kind.is_public() && rhs.kind.is_public() {
300 return self.ir.derived_alloc.alloc();
301 }
302 let out = self.ir.private_alloc.alloc();
303 self.ir.private_wires_status.push(WireStatus::Unknown);
304 self.ir
305 .zero_constraints
306 .push(Operand::new(smallvec![lhs, rhs, out]));
307 out
308 }
309
310 fn mul(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire {
311 if lhs.kind.is_public() && rhs.kind.is_public() {
314 return self.ir.derived_alloc.alloc();
315 }
316 let out = self.ir.private_alloc.alloc();
317 self.ir.private_wires_status.push(WireStatus::Unknown);
318 self.ir.mul_constraints.push(MulConstraint {
319 a: lhs.into(),
320 b: rhs.into(),
321 c: out.into(),
322 });
323 out
324 }
325
326 fn hint<H: Fn([F; IN]) -> [F; OUT], const IN: usize, const OUT: usize>(
327 &mut self,
328 inputs: [Self::Wire; IN],
329 _f: H,
330 ) -> [Self::Wire; OUT] {
331 let derived = inputs.iter().all(|wire| wire.kind.is_public());
334 array::from_fn(|_| {
335 if derived {
336 self.ir.derived_alloc.alloc()
337 } else {
338 let wire = self.ir.private_alloc.alloc();
339 self.ir.private_wires_status.push(WireStatus::Unknown);
340 wire
341 }
342 })
343 }
344
345 fn hint_varsize<H: FnOnce(&[F]) -> Vec<F>>(
346 &mut self,
347 inputs: &[Self::Wire],
348 out_len: usize,
349 _f: H,
350 ) -> Vec<Self::Wire> {
351 let derived = inputs.iter().all(|wire| wire.kind.is_public());
354 (0..out_len)
355 .map(|_| {
356 if derived {
357 self.ir.derived_alloc.alloc()
358 } else {
359 let wire = self.ir.private_alloc.alloc();
360 self.ir.private_wires_status.push(WireStatus::Unknown);
361 wire
362 }
363 })
364 .collect()
365 }
366}
367
368#[derive(Debug)]
369pub struct WitnessError {
370 pub backtrace: Backtrace,
371}
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq)]
374pub struct WitnessWire<F: Field> {
375 val: F,
376 segment: WitnessSegment,
377}
378
379impl<F: Field> WitnessWire<F> {
380 #[inline]
381 pub const fn val(self) -> F {
382 self.val
383 }
384
385 #[inline]
388 fn is_public(self) -> bool {
389 self.segment == WitnessSegment::Public
390 }
391}
392
393#[derive(Debug)]
406pub struct WitnessGenerator<F: Field, LayoutRef = Arc<WitnessLayout<F>>> {
407 derived_alloc: WireAllocator,
408 private_alloc: WireAllocator,
409 public: Vec<F>,
410 precommit: Vec<F>,
411 private: Vec<F>,
412 layout: LayoutRef,
413 first_error: Option<Backtrace>,
414}
415
416impl<F: Field, LayoutRef: Deref<Target = WitnessLayout<F>>> WitnessGenerator<F, LayoutRef> {
417 pub fn new(layout: LayoutRef) -> Self {
418 let mut public = zeroed_vec(layout.public_size());
419 public[..layout.constants.len()].copy_from_slice(&layout.constants);
420
421 let precommit = zeroed_vec(layout.precommit_size());
422 let private = zeroed_vec(layout.private_size());
423
424 Self {
425 derived_alloc: WireAllocator::new(WireKind::Derived),
426 private_alloc: WireAllocator::new(WireKind::Private),
427 public,
428 precommit,
429 private,
430 layout,
431 first_error: None,
432 }
433 }
434
435 fn alloc_op_value(&mut self, all_derivable: bool, value: F) -> WitnessWire<F> {
438 let wire = if all_derivable {
439 self.derived_alloc.alloc()
440 } else {
441 self.private_alloc.alloc()
442 };
443 self.write_value(wire, value)
444 }
445
446 fn write_value(&mut self, wire: ConstraintWire, value: F) -> WitnessWire<F> {
447 if let Some(index) = self.layout.get(&wire) {
448 match index.segment {
449 WitnessSegment::Public => self.public[index.index as usize] = value,
450 WitnessSegment::Precommit => self.precommit[index.index as usize] = value,
451 WitnessSegment::Private => self.private[index.index as usize] = value,
452 }
453 }
454 WitnessWire {
455 val: value,
456 segment: wire.kind.segment(),
457 }
458 }
459
460 pub fn write_inout(&mut self, wire: ConstraintWire, value: F) -> WitnessWire<F> {
461 assert_eq!(wire.kind, WireKind::InOut);
462 self.write_value(wire, value)
463 }
464
465 pub fn write_precommit(&mut self, wire: ConstraintWire, value: F) -> WitnessWire<F> {
466 assert_eq!(wire.kind, WireKind::Precommit);
467 self.write_value(wire, value)
468 }
469
470 pub fn build(self) -> Result<Witness<F>, WitnessError> {
471 if let Some(backtrace) = self.first_error {
472 Err(WitnessError { backtrace })
473 } else {
474 Ok(Witness::new(self.public, self.precommit, self.private))
475 }
476 }
477
478 pub const fn error(&self) -> Option<&Backtrace> {
479 self.first_error.as_ref()
480 }
481
482 fn record_error(&mut self) {
483 if self.first_error.is_none() {
484 self.first_error = Some(Backtrace::capture());
485 }
486 }
487}
488
489impl<F: Field, LayoutRef: Deref<Target = WitnessLayout<F>>> CircuitBuilder
490 for WitnessGenerator<F, LayoutRef>
491{
492 type Wire = WitnessWire<F>;
493 type Field = F;
494
495 fn assert_zero(&mut self, wire: Self::Wire) {
496 if wire.val() != F::ZERO {
497 self.record_error();
498 }
499 }
500
501 fn assert_eq(&mut self, lhs: Self::Wire, rhs: Self::Wire) {
502 if lhs.val() != rhs.val() {
503 self.record_error();
504 }
505 }
506
507 fn constant(&mut self, val: F) -> Self::Wire {
508 WitnessWire {
509 val,
510 segment: WitnessSegment::Public,
511 }
512 }
513
514 fn add(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire {
515 let all_derivable = lhs.is_public() && rhs.is_public();
516 self.alloc_op_value(all_derivable, lhs.val() + rhs.val())
517 }
518
519 fn mul(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire {
520 let all_derivable = lhs.is_public() && rhs.is_public();
521 self.alloc_op_value(all_derivable, lhs.val() * rhs.val())
522 }
523
524 fn hint<H: Fn([F; IN]) -> [F; OUT], const IN: usize, const OUT: usize>(
525 &mut self,
526 inputs: [Self::Wire; IN],
527 f: H,
528 ) -> [Self::Wire; OUT] {
529 let all_derivable = inputs.iter().all(|wire| wire.is_public());
530 f(inputs.map(WitnessWire::val)).map(|value| self.alloc_op_value(all_derivable, value))
531 }
532
533 fn hint_varsize<H: FnOnce(&[F]) -> Vec<F>>(
534 &mut self,
535 inputs: &[Self::Wire],
536 out_len: usize,
537 f: H,
538 ) -> Vec<Self::Wire> {
539 let all_derivable = inputs.iter().all(|wire| wire.is_public());
540 let input_vals: Vec<F> = inputs.iter().map(|wire| wire.val()).collect();
541 let outputs = f(&input_vals);
542 debug_assert_eq!(
543 outputs.len(),
544 out_len,
545 "hint_varsize closure returned wrong output count"
546 );
547 outputs
548 .into_iter()
549 .map(|value| self.alloc_op_value(all_derivable, value))
550 .collect()
551 }
552}
553
554#[derive(Debug, Clone, Copy, PartialEq, Eq)]
559pub struct PublicWire<F: Field>(Option<F>);
560
561impl<F: Field> PublicWire<F> {
562 #[inline]
565 pub const fn value(self) -> Option<F> {
566 self.0
567 }
568}
569
570#[derive(Debug)]
587pub struct InstanceGenerator<F: Field, LayoutRef = Arc<WitnessLayout<F>>> {
588 derived_alloc: WireAllocator,
589 public: Vec<F>,
590 layout: LayoutRef,
591}
592
593impl<F: Field, LayoutRef: Deref<Target = WitnessLayout<F>>> InstanceGenerator<F, LayoutRef> {
594 pub fn new(layout: LayoutRef) -> Self {
595 let mut public = zeroed_vec(layout.public_size());
596 public[..layout.constants.len()].copy_from_slice(&layout.constants);
597
598 Self {
599 derived_alloc: WireAllocator::new(WireKind::Derived),
600 public,
601 layout,
602 }
603 }
604
605 pub fn write_inout(&mut self, wire: ConstraintWire, value: F) -> PublicWire<F> {
607 assert_eq!(wire.kind, WireKind::InOut);
608 self.write_public(wire, value)
609 }
610
611 pub fn placeholder_precommit(&mut self, wire: ConstraintWire) -> PublicWire<F> {
614 assert_eq!(wire.kind, WireKind::Precommit);
615 PublicWire(None)
616 }
617
618 fn write_public(&mut self, wire: ConstraintWire, value: F) -> PublicWire<F> {
621 if let Some(index) = self.layout.get(&wire) {
622 debug_assert_eq!(index.segment, WitnessSegment::Public);
623 self.public[index.index as usize] = value;
624 }
625 PublicWire(Some(value))
626 }
627
628 pub fn public(&self) -> &[F] {
633 &self.public
634 }
635
636 pub fn build(self) -> Vec<F> {
639 self.public
640 }
641}
642
643impl<F: Field, LayoutRef: Deref<Target = WitnessLayout<F>>> CircuitBuilder
644 for InstanceGenerator<F, LayoutRef>
645{
646 type Wire = PublicWire<F>;
647 type Field = F;
648
649 fn assert_zero(&mut self, _wire: Self::Wire) {}
652
653 fn assert_eq(&mut self, _lhs: Self::Wire, _rhs: Self::Wire) {}
654
655 fn constant(&mut self, val: F) -> Self::Wire {
656 PublicWire(Some(val))
657 }
658
659 fn add(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire {
660 match (lhs.0, rhs.0) {
661 (Some(lhs), Some(rhs)) => {
662 let wire = self.derived_alloc.alloc();
663 self.write_public(wire, lhs + rhs)
664 }
665 _ => PublicWire(None),
666 }
667 }
668
669 fn mul(&mut self, lhs: Self::Wire, rhs: Self::Wire) -> Self::Wire {
670 match (lhs.0, rhs.0) {
671 (Some(lhs), Some(rhs)) => {
672 let wire = self.derived_alloc.alloc();
673 self.write_public(wire, lhs * rhs)
674 }
675 _ => PublicWire(None),
676 }
677 }
678
679 fn hint<H: Fn([F; IN]) -> [F; OUT], const IN: usize, const OUT: usize>(
680 &mut self,
681 inputs: [Self::Wire; IN],
682 f: H,
683 ) -> [Self::Wire; OUT] {
684 if inputs.iter().all(|wire| wire.0.is_some()) {
687 let values = inputs.map(|wire| wire.0.expect("every input checked public above"));
688 f(values).map(|value| {
689 let wire = self.derived_alloc.alloc();
690 self.write_public(wire, value)
691 })
692 } else {
693 [PublicWire(None); OUT]
694 }
695 }
696
697 fn hint_varsize<H: FnOnce(&[F]) -> Vec<F>>(
698 &mut self,
699 inputs: &[Self::Wire],
700 out_len: usize,
701 f: H,
702 ) -> Vec<Self::Wire> {
703 if inputs.iter().all(|wire| wire.0.is_some()) {
707 let input_vals: Vec<F> = inputs
708 .iter()
709 .map(|wire| wire.0.expect("every input checked public above"))
710 .collect();
711 let outputs = f(&input_vals);
712 debug_assert_eq!(
713 outputs.len(),
714 out_len,
715 "hint_varsize closure returned wrong output count"
716 );
717 outputs
718 .into_iter()
719 .map(|value| {
720 let wire = self.derived_alloc.alloc();
721 self.write_public(wire, value)
722 })
723 .collect()
724 } else {
725 vec![PublicWire(None); out_len]
726 }
727 }
728}
729
730#[cfg(test)]
731mod tests {
732 use std::iter::successors;
733
734 use binius_field::{Field, Ghash128b as B128, PackedField};
735
736 use super::*;
737
738 fn fibonacci<Builder: CircuitBuilder>(
739 builder: &mut Builder,
740 x0: Builder::Wire,
741 x1: Builder::Wire,
742 n: usize,
743 ) -> Builder::Wire {
744 if n == 0 {
745 return x0;
746 }
747
748 let (_xnsub1, xn) = successors(Some((x0, x1)), |&(a, b)| {
749 let next = builder.mul(a, b);
750 Some((b, next))
751 })
752 .nth(n - 1)
753 .expect("closure always returns Some");
754
755 xn
756 }
757
758 #[test]
759 fn test_fibonacci() {
760 let mut constraint_builder = ConstraintBuilder::new();
761 let x0 = constraint_builder.alloc_inout();
762 let x1 = constraint_builder.alloc_inout();
763 let xn = constraint_builder.alloc_inout();
764 let out = fibonacci(&mut constraint_builder, x0, x1, 20);
765 constraint_builder.assert_eq(out, xn);
766 let ir = constraint_builder.build();
767 let (constraint_system, layout) = ir.finalize();
768
769 let mut witness_generator = WitnessGenerator::new(&layout);
770 let x0 = witness_generator.write_inout(x0, B128::ONE);
771 let x1 = witness_generator.write_inout(x1, B128::MULTIPLICATIVE_GENERATOR);
772 let xn = witness_generator.write_inout(xn, B128::MULTIPLICATIVE_GENERATOR.pow(6765));
773 let out = fibonacci(&mut witness_generator, x0, x1, 20);
774 witness_generator.assert_eq(out, xn);
775 let witness = witness_generator.build().unwrap();
776
777 constraint_system.validate(&witness);
778 }
779
780 #[test]
781 fn test_fibonacci_with_precommit() {
782 let mut constraint_builder = ConstraintBuilder::new();
783 let x0 = constraint_builder.alloc_inout();
784 let x1 = constraint_builder.alloc_inout();
785 let xn = constraint_builder.alloc_precommit();
786 let out = fibonacci(&mut constraint_builder, x0, x1, 20);
787 constraint_builder.assert_eq(out, xn);
788 let ir = constraint_builder.build();
789 let (constraint_system, layout) = ir.finalize();
790
791 let mut witness_generator = WitnessGenerator::new(&layout);
792 let x0 = witness_generator.write_inout(x0, B128::ONE);
793 let x1 = witness_generator.write_inout(x1, B128::MULTIPLICATIVE_GENERATOR);
794 let xn = witness_generator.write_precommit(xn, B128::MULTIPLICATIVE_GENERATOR.pow(6765));
795 let out = fibonacci(&mut witness_generator, x0, x1, 20);
796 witness_generator.assert_eq(out, xn);
797 let witness = witness_generator.build().unwrap();
798
799 constraint_system.validate(&witness);
800 }
801
802 #[test]
803 fn test_assertion_failure_captured() {
804 let mut constraint_builder = ConstraintBuilder::new();
805 let x = constraint_builder.alloc_inout();
806 let y = constraint_builder.alloc_inout();
807 let sum = constraint_builder.add(x, y);
808 let expected = constraint_builder.alloc_inout();
809 constraint_builder.assert_eq(sum, expected);
810 let ir = constraint_builder.build();
811 let (_constraint_system, layout) = ir.finalize();
812
813 let mut witness_generator = WitnessGenerator::new(&layout);
814 let x_val = B128::new(5);
815 let y_val = B128::new(7);
816 let wrong_expected = B128::new(99); let x_wire = witness_generator.write_inout(x, x_val);
819 let y_wire = witness_generator.write_inout(y, y_val);
820 let sum_wire = witness_generator.add(x_wire, y_wire);
821 let expected_wire = witness_generator.write_inout(expected, wrong_expected);
822 witness_generator.assert_eq(sum_wire, expected_wire);
823
824 assert!(witness_generator.build().is_err());
825 }
826
827 #[test]
828 fn test_zero_constant_not_in_final_operands() {
829 use crate::{compiler::compile, constraint_system::WitnessSegment};
830
831 let mut builder = ConstraintBuilder::new();
834 let x = builder.alloc_inout();
835 let zero = builder.constant(B128::ZERO);
836 let sum = builder.add(x, zero);
837 let y = builder.alloc_inout();
838 builder.assert_eq(sum, y);
839
840 let (cs, _layout) = compile(builder);
841
842 let zero_constant_indices: std::collections::HashSet<u32> = cs
843 .constants()
844 .iter()
845 .enumerate()
846 .filter(|&(_, c)| *c == B128::ZERO)
847 .map(|(i, _)| i as u32)
848 .collect();
849
850 for constraint in cs.mul_constraints() {
851 for operand in [&constraint.a, &constraint.b, &constraint.c] {
852 for wire_idx in operand.wires() {
853 assert!(
854 !(wire_idx.segment == WitnessSegment::Public
855 && zero_constant_indices.contains(&wire_idx.index)),
856 "zero constant found in compiled operand at WitnessIndex::public({})",
857 wire_idx.index
858 );
859 }
860 }
861 }
862 }
863
864 fn mixed_circuit<Builder: CircuitBuilder>(
870 builder: &mut Builder,
871 a: Builder::Wire,
872 b: Builder::Wire,
873 s: Builder::Wire,
874 expected: Builder::Wire,
875 ) {
876 use binius_field::arithmetic_traits::InvertOrZero;
877
878 let d = builder.mul(a, b); let [d_inv] = builder.hint([d], |[x]| [x.invert_or_zero()]); let one_check = builder.mul(d, d_inv); let e = builder.add(one_check, b); let p = builder.mul(e, s); builder.assert_eq(p, expected);
884 }
885
886 fn mixed_expected(a: B128, b: B128, s: B128) -> B128 {
888 use binius_field::arithmetic_traits::InvertOrZero;
889 let d = a * b;
890 let one_check = d * d.invert_or_zero();
891 (one_check + b) * s
892 }
893
894 #[test]
895 fn test_instance_generator_syncs_with_witness() {
896 use crate::compiler::compile;
897
898 let a_val = B128::new(3);
899 let b_val = B128::new(5);
900 let s_val = B128::new(7);
901 let expected_val = mixed_expected(a_val, b_val, s_val);
902
903 let mut cb = ConstraintBuilder::new();
904 let a = cb.alloc_inout();
905 let b = cb.alloc_inout();
906 let s = cb.alloc_precommit();
907 let expected = cb.alloc_inout();
908 mixed_circuit(&mut cb, a, b, s, expected);
909 let (cs, layout) = compile(cb);
910
911 let mut wg = WitnessGenerator::new(&layout);
913 let a_w = wg.write_inout(a, a_val);
914 let b_w = wg.write_inout(b, b_val);
915 let s_w = wg.write_precommit(s, s_val);
916 let expected_w = wg.write_inout(expected, expected_val);
917 mixed_circuit(&mut wg, a_w, b_w, s_w, expected_w);
918 let witness = wg.build().expect("witness generation should succeed");
919 cs.validate(&witness);
920
921 let mut ig = InstanceGenerator::new(&layout);
924 let a_i = ig.write_inout(a, a_val);
925 let b_i = ig.write_inout(b, b_val);
926 let s_i = ig.placeholder_precommit(s);
927 let expected_i = ig.write_inout(expected, expected_val);
928 mixed_circuit(&mut ig, a_i, b_i, s_i, expected_i);
929 let public = ig.build();
930
931 assert_eq!(public, witness.public());
932 }
933
934 #[test]
935 fn test_derived_elision_no_private_wires() {
936 use crate::compiler::compile;
937
938 let mut cb = ConstraintBuilder::new();
940 let x = cb.alloc_inout();
941 let y = cb.alloc_inout();
942 let x2 = cb.mul(x, x); let x3 = cb.mul(x2, x); cb.assert_eq(x3, y);
945 assert_eq!(x2.kind, WireKind::Derived);
946 assert_eq!(x3.kind, WireKind::Derived);
947 let (cs, layout) = compile(cb);
948
949 assert_eq!(cs.n_private(), 0);
952 assert_eq!(layout.n_derived(), 1);
953
954 let x_val = B128::new(9);
955 let y_val = x_val * x_val * x_val;
956
957 let mut wg = WitnessGenerator::new(&layout);
958 let x_w = wg.write_inout(x, x_val);
959 let y_w = wg.write_inout(y, y_val);
960 let x2_w = wg.mul(x_w, x_w);
961 let x3_w = wg.mul(x2_w, x_w);
962 wg.assert_eq(x3_w, y_w);
963 let witness = wg.build().expect("witness generation should succeed");
964 cs.validate(&witness);
965 }
966
967 #[test]
968 fn test_derived_wire_in_constraint_maps_to_public() {
969 use crate::{compiler::compile, constraint_system::WitnessSegment};
970
971 let mut cb = ConstraintBuilder::new();
973 let a = cb.alloc_inout();
974 let b = cb.alloc_inout();
975 let s = cb.alloc_precommit();
976 let e = cb.add(a, b); let p = cb.mul(e, s); cb.assert_zero(p);
979 assert_eq!(e.kind, WireKind::Derived);
980 let (cs, layout) = compile(cb);
981
982 let e_index = layout.get(&e).expect("alive derived wire must have a slot");
984 assert_eq!(e_index.segment, WitnessSegment::Public);
985
986 let references_e = cs.mul_constraints().iter().any(|c| {
988 [&c.a, &c.b, &c.c]
989 .iter()
990 .any(|operand| operand.wires().contains(&e_index))
991 });
992 assert!(references_e, "derived wire's public index should appear in a mul constraint");
993
994 let a_val = B128::new(3);
996 let b_val = B128::new(5);
997 let s_val = B128::ZERO; let mut wg = WitnessGenerator::new(&layout);
999 let a_w = wg.write_inout(a, a_val);
1000 let b_w = wg.write_inout(b, b_val);
1001 let s_w = wg.write_precommit(s, s_val);
1002 let e_w = wg.add(a_w, b_w);
1003 let p_w = wg.mul(e_w, s_w);
1004 wg.assert_zero(p_w);
1005 let witness = wg.build().expect("witness generation should succeed");
1006 cs.validate(&witness);
1007 }
1008
1009 #[test]
1010 fn test_pruned_derived_intermediate_has_no_slot() {
1011 use crate::compiler::compile;
1012
1013 let mut cb = ConstraintBuilder::<B128>::new();
1015 let x = cb.alloc_inout();
1016 let y = cb.alloc_inout();
1017 let x2 = cb.mul(x, x); let x3 = cb.mul(x2, x); cb.assert_eq(x3, y);
1020 let (_cs, layout) = compile(cb);
1021
1022 assert!(layout.get(&x2).is_none(), "pruned derived intermediate must have no slot");
1024 assert!(layout.get(&x3).is_some(), "referenced derived wire must have a slot");
1025 }
1026
1027 #[test]
1028 fn test_hint_varsize_syncs_with_witness() {
1029 use crate::compiler::compile;
1030
1031 fn circuit<Builder: CircuitBuilder>(
1035 builder: &mut Builder,
1036 a: Builder::Wire,
1037 b: Builder::Wire,
1038 expected: Builder::Wire,
1039 ) {
1040 let outs =
1041 builder.hint_varsize(&[a, b], 2, |vals| vec![vals[0] + vals[1], vals[0] * vals[1]]);
1042 builder.assert_eq(outs[0], expected);
1043 }
1044
1045 let a_val = B128::new(3);
1046 let b_val = B128::new(5);
1047 let expected_val = a_val + b_val;
1048
1049 let mut cb = ConstraintBuilder::new();
1050 let a = cb.alloc_inout();
1051 let b = cb.alloc_inout();
1052 let expected = cb.alloc_inout();
1053 circuit(&mut cb, a, b, expected);
1054 let (cs, layout) = compile(cb);
1055
1056 let mut wg = WitnessGenerator::new(&layout);
1057 let a_w = wg.write_inout(a, a_val);
1058 let b_w = wg.write_inout(b, b_val);
1059 let expected_w = wg.write_inout(expected, expected_val);
1060 circuit(&mut wg, a_w, b_w, expected_w);
1061 let witness = wg.build().expect("witness generation should succeed");
1062 cs.validate(&witness);
1063
1064 let mut ig = InstanceGenerator::new(&layout);
1065 let a_i = ig.write_inout(a, a_val);
1066 let b_i = ig.write_inout(b, b_val);
1067 let expected_i = ig.write_inout(expected, expected_val);
1068 circuit(&mut ig, a_i, b_i, expected_i);
1069 let public = ig.build();
1070
1071 assert_eq!(public, witness.public());
1072 }
1073}