binius_examples/circuits/
subset_sum.rs1use binius_core::Word;
3use binius_frontend::{CircuitBuilder, Wire, WitnessFiller};
4
5pub struct SubsetSum {
14 len: usize,
18 values: Vec<Wire>,
20 target: Wire,
22 selection: Vec<Wire>,
24}
25
26impl SubsetSum {
27 pub fn construct_circuit(builder: &mut CircuitBuilder, len: usize) -> Self {
31 let mut values = Vec::new();
33 for _ in 0..len {
34 values.push(builder.add_inout());
35 }
36
37 let target = builder.add_inout();
39
40 let mut selection = Vec::new();
42 for _ in 0..len {
43 selection.push(builder.add_witness());
44 }
45
46 let mut values_masked = Vec::new();
48 for i in 0..len {
49 let bit_mask = builder.sar(selection[i], 63);
52 let value_masked = builder.band(values[i], bit_mask);
53 values_masked.push(value_masked);
54 }
55
56 let mut sum = builder.add_constant(Word::ZERO);
58 let mut carry = builder.add_constant(Word::ZERO);
59 for i in 0..len {
60 (sum, carry) = builder.iadd_cin_cout(sum, values_masked[i], carry);
61 builder.assert_false("no overflow", carry);
63 }
64
65 builder.assert_eq("sum matches target", sum, target);
67
68 Self {
69 len,
70 values,
71 target,
72 selection,
73 }
74 }
75
76 pub fn populate_problem(&self, filler: &mut WitnessFiller<'_>, values: &[u64], target: u64) {
81 assert_eq!(values.len(), self.len);
82
83 for i in 0..self.len {
84 filler[self.values[i]] = Word(values[i]);
85 }
86
87 filler[self.target] = Word(target);
88 }
89
90 pub fn populate_solution(&self, filler: &mut WitnessFiller<'_>, selection: &[bool]) {
96 assert_eq!(selection.len(), self.len);
97
98 for i in 0..self.len {
99 let word = if selection[i] {
100 Word::ALL_ONE
101 } else {
102 Word::ZERO
103 };
104 filler[self.selection[i]] = word;
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111
112 use super::*;
113
114 #[test]
116 fn test_valid() {
117 let mut builder = CircuitBuilder::new();
119 let subset_sum = SubsetSum::construct_circuit(&mut builder, 5);
120 let circuit = builder.build();
121
122 let mut filler = circuit.new_witness_filler();
124 subset_sum.populate_problem(&mut filler, &[2, 5, 5, 3, 7], 17);
125 subset_sum.populate_solution(&mut filler, &[false, true, true, false, true]);
126 circuit.populate_wire_witness(&mut filler).unwrap();
127
128 let constraint_system = circuit.constraint_system();
130 constraint_system.verify(&filler.into_value_vec()).unwrap();
131 }
132
133 #[test]
135 fn test_invalid() {
136 let mut builder = CircuitBuilder::new();
138 let subset_sum = SubsetSum::construct_circuit(&mut builder, 5);
139 let circuit = builder.build();
140
141 let mut filler = circuit.new_witness_filler();
143 subset_sum.populate_problem(&mut filler, &[2, 5, 5, 3, 7], 17);
144 subset_sum.populate_solution(&mut filler, &[false, true, true, false, false]);
145 circuit.populate_wire_witness(&mut filler).unwrap_err();
146 }
147
148 #[test]
151 fn test_weird_booleans() {
152 let mut builder = CircuitBuilder::new();
154 let subset_sum = SubsetSum::construct_circuit(&mut builder, 1);
155 let circuit = builder.build();
156
157 let mut filler = circuit.new_witness_filler();
159 subset_sum.populate_problem(&mut filler, &[3], 1);
160 filler[subset_sum.selection[0]] = Word(1);
165 circuit.populate_wire_witness(&mut filler).unwrap_err();
166 }
167
168 #[test]
170 fn test_overflow() {
171 let mut builder = CircuitBuilder::new();
173 let subset_sum = SubsetSum::construct_circuit(&mut builder, 2);
174 let circuit = builder.build();
175
176 let mut filler = circuit.new_witness_filler();
178 subset_sum.populate_problem(&mut filler, &[2 << 62, 3 << 62], 1 << 62);
179 subset_sum.populate_solution(&mut filler, &[true, true]);
182 circuit.populate_wire_witness(&mut filler).unwrap_err();
183 }
184}