Skip to content

Commit d93c2ef

Browse files
committed
examples: Transparent columns usage (part 1) (#8)
* feat: Add example of Transparent (Constant) column usage * example: Add example of Transparent (Powers) column usage * example: Add example of Transparent (DisjointProduct) column usage * example: Add example of Transparent (EqIndPartialEval) column usage
1 parent 869a0e5 commit d93c2ef

5 files changed

Lines changed: 326 additions & 0 deletions

File tree

examples/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,22 @@ path = "acc-repeated.rs"
104104
name = "acc-zeropadded"
105105
path = "acc-zeropadded.rs"
106106

107+
[[example]]
108+
name = "acc-powers"
109+
path = "acc-powers.rs"
110+
111+
[[example]]
112+
name = "acc-constants"
113+
path = "acc-constants.rs"
114+
115+
[[example]]
116+
name = "acc-disjoint-product"
117+
path = "acc-disjoint-product.rs"
118+
119+
[[example]]
120+
name = "acc-eq-ind-partial-eval"
121+
path = "acc-eq-ind-partial-eval.rs"
122+
107123
[lints.clippy]
108124
needless_range_loop = "allow"
109125

examples/acc-constants.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use binius_circuits::{builder::ConstraintSystemBuilder, sha256::u32const_repeating};
2+
use binius_core::{
3+
constraint_system::validate::validate_witness, oracle::OracleId,
4+
transparent::constant::Constant,
5+
};
6+
use binius_field::{arch::OptimalUnderlier, BinaryField128b, BinaryField1b, BinaryField32b};
7+
8+
type U = OptimalUnderlier;
9+
type F128 = BinaryField128b;
10+
type F32 = BinaryField32b;
11+
type F1 = BinaryField1b;
12+
13+
const LOG_SIZE: usize = 4;
14+
15+
// FIXME: Following gadgets are unconstrained. Only for demonstrative purpose, don't use in production
16+
17+
fn constants_gadget(
18+
name: impl ToString,
19+
log_size: usize,
20+
builder: &mut ConstraintSystemBuilder<U, F128>,
21+
constant_value: u32,
22+
) -> OracleId {
23+
builder.push_namespace(name);
24+
25+
let c = Constant::new(log_size, F32::new(constant_value));
26+
27+
let oracle = builder.add_transparent("constant", c).unwrap();
28+
29+
if let Some(witness) = builder.witness() {
30+
let mut oracle_witness = witness.new_column::<F32>(oracle);
31+
let values = oracle_witness.as_mut_slice::<u32>();
32+
for v in values {
33+
*v = constant_value;
34+
}
35+
}
36+
37+
builder.pop_namespace();
38+
39+
oracle
40+
}
41+
42+
// Transparent column can also naturally be used for storing some constants (also available for verifier).
43+
// For example there is a 'u32const_repeating' function (in sha256 gadget) that does exactly this
44+
// using Transparent + Repeated columns. Alternatively one can use Constant abstraction to create equivalent
45+
// Transparent column.
46+
fn main() {
47+
let allocator = bumpalo::Bump::new();
48+
let mut builder = ConstraintSystemBuilder::<U, F128>::new_with_witness(&allocator);
49+
50+
pub const SHA256_INIT: [u32; 8] = [
51+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
52+
0x5be0cd19,
53+
];
54+
55+
let oracles: [OracleId; 8] =
56+
SHA256_INIT.map(|c| u32const_repeating(LOG_SIZE, &mut builder, c, "INIT").unwrap());
57+
if let Some(witness) = builder.witness() {
58+
for (index, oracle) in oracles.into_iter().enumerate() {
59+
let values = witness.get::<F1>(oracle).unwrap().as_slice::<u32>();
60+
61+
// every value in the column should match the expected one
62+
for value in values {
63+
assert_eq!(*value, SHA256_INIT[index]);
64+
}
65+
}
66+
}
67+
68+
let oracles: [OracleId; 8] =
69+
SHA256_INIT.map(|c| constants_gadget("constants_gadget", LOG_SIZE, &mut builder, c));
70+
if let Some(witness) = builder.witness() {
71+
for (index, oracle) in oracles.into_iter().enumerate() {
72+
// The difference is here. With Constant we have to operate over F32, while
73+
// with Transparent + Repeated approach as in 'u32const_repeating' we operate over F1,
74+
// which can be more convenient in the bit-oriented computations
75+
let values = witness.get::<F32>(oracle).unwrap().as_slice::<u32>();
76+
77+
// every value in the column should match the expected one
78+
for value in values {
79+
assert_eq!(*value, SHA256_INIT[index]);
80+
}
81+
}
82+
}
83+
84+
let witness = builder.take_witness().unwrap();
85+
let constraints_system = builder.build().unwrap();
86+
87+
validate_witness(&constraints_system, &[], &witness).unwrap();
88+
}

examples/acc-disjoint-product.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use binius_circuits::builder::ConstraintSystemBuilder;
2+
use binius_core::{
3+
constraint_system::validate::validate_witness,
4+
transparent::{constant::Constant, disjoint_product::DisjointProduct, powers::Powers},
5+
};
6+
use binius_field::{
7+
arch::OptimalUnderlier, BinaryField, BinaryField128b, BinaryField8b, PackedField,
8+
};
9+
10+
type U = OptimalUnderlier;
11+
type F128 = BinaryField128b;
12+
type F8 = BinaryField8b;
13+
14+
const LOG_SIZE: usize = 4;
15+
16+
// FIXME: Following gadgets are unconstrained. Only for demonstrative purpose, don't use in production
17+
18+
// DisjointProduct can be used for creating some more elaborated regularities over public data.
19+
// In the following example we have a Transparent column with DisjointProduct instantiated over Powers
20+
// and Constant. In this regularity, the DisjointProduct would be represented as a following expression:
21+
//
22+
// [ c * F8(x)^0, c * F8(x)^1, c * F8(x)^2, ... c * F8(x)^(2^LOG_SIZE) ],
23+
//
24+
// where
25+
// 'x' is a multiplicative generator - a public value that exists for every BinaryField,
26+
// 'c' is some (F8) constant.
27+
//
28+
// Also note, that DisjointProduct makes eventual Transparent column to have height (n_vars) which is sum
29+
// of heights (n_vars) of Powers and Constant, so actual data could be repeated multiple times
30+
fn main() {
31+
let allocator = bumpalo::Bump::new();
32+
let mut builder = ConstraintSystemBuilder::<U, F128>::new_with_witness(&allocator);
33+
34+
let generator = F8::MULTIPLICATIVE_GENERATOR;
35+
let powers = Powers::new(LOG_SIZE, generator.into());
36+
37+
let constant_value = F8::new(0xf0);
38+
let constant = Constant::new(LOG_SIZE, constant_value);
39+
40+
let disjoint_product = DisjointProduct(powers, constant);
41+
let disjoint_product_id = builder
42+
.add_transparent("disjoint_product", disjoint_product)
43+
.unwrap();
44+
45+
if let Some(witness) = builder.witness() {
46+
let mut disjoint_product_witness = witness.new_column::<F8>(disjoint_product_id);
47+
48+
let values = disjoint_product_witness.as_mut_slice::<F8>();
49+
50+
let mut exponent = 0u64;
51+
for val in values.iter_mut() {
52+
if exponent == 2u64.pow(LOG_SIZE as u32) {
53+
exponent = 0;
54+
}
55+
*val = generator.pow(exponent) * constant_value;
56+
exponent += 1;
57+
}
58+
}
59+
60+
let witness = builder.take_witness().unwrap();
61+
let constraints_system = builder.build().unwrap();
62+
63+
validate_witness(&constraints_system, &[], &witness).unwrap();
64+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use binius_circuits::builder::ConstraintSystemBuilder;
2+
use binius_core::{
3+
constraint_system::validate::validate_witness, transparent::eq_ind::EqIndPartialEval,
4+
};
5+
use binius_field::{arch::OptimalUnderlier, BinaryField128b, PackedField};
6+
7+
type U = OptimalUnderlier;
8+
type F128 = BinaryField128b;
9+
10+
const LOG_SIZE: usize = 3;
11+
12+
// FIXME: Following gadgets are unconstrained. Only for demonstrative purpose, don't use in production
13+
14+
// Currently, it is hard for me to imagine some real world use-cases where Transparent column specified by
15+
// EqIndPartialEval could be useful. The program can use some of its data as challenges and the Transparent
16+
// column with EqIndPartialEval will expect witness values defined as following:
17+
//
18+
// x_i * y_i + (1 - x_i) * (1 - y_i)
19+
//
20+
// where 'x_i' is an element from a particular row of basis matrix, and y_i is a given challenge.
21+
//
22+
fn main() {
23+
let allocator = bumpalo::Bump::new();
24+
let mut builder = ConstraintSystemBuilder::<U, F128>::new_with_witness(&allocator);
25+
26+
// A truth table [000, 001, 010, 011 ... 111] where each row is in reversed order
27+
let rev_basis = [
28+
vec![0, 0, 0],
29+
vec![1, 0, 0],
30+
vec![0, 1, 0],
31+
vec![1, 1, 0],
32+
vec![0, 0, 1],
33+
vec![1, 0, 1],
34+
vec![0, 1, 1],
35+
vec![1, 1, 1],
36+
];
37+
38+
// rev_basis size correlates with LOG_SIZE
39+
assert_eq!(1 << LOG_SIZE, rev_basis.len());
40+
41+
// let's choose some random challenges (each not greater than 1 << LOG_SIZE bits for this example)
42+
let challenges = vec![F128::from(110), F128::from(190), F128::from(200)];
43+
44+
// challenges size correlates with LOG_SIZE
45+
assert_eq!(challenges.len(), LOG_SIZE);
46+
47+
let eq_ind_partial_eval = EqIndPartialEval::new(LOG_SIZE, challenges.clone()).unwrap();
48+
49+
let id = builder
50+
.add_transparent("eq_ind_partial_eval", eq_ind_partial_eval)
51+
.unwrap();
52+
53+
if let Some(witness) = builder.witness() {
54+
let mut eq_witness = witness.new_column::<F128>(id);
55+
56+
let column_values = eq_witness.as_mut_slice::<F128>();
57+
assert_eq!(column_values.len(), 1 << LOG_SIZE);
58+
59+
let one = F128::one();
60+
61+
for (inv_basis_item, val) in rev_basis.iter().zip(column_values.iter_mut()) {
62+
let mut value = F128::one();
63+
inv_basis_item
64+
.iter()
65+
.zip(challenges.iter())
66+
.for_each(|(x, y)| {
67+
let x = F128::new(*x);
68+
let y = *y;
69+
70+
// following expression is defined in the EqIndPartialEval implementation
71+
value *= x * y + (one - x) * (one - y);
72+
});
73+
*val = value;
74+
}
75+
}
76+
77+
let witness = builder.take_witness().unwrap();
78+
let constraints_system = builder.build().unwrap();
79+
80+
validate_witness(&constraints_system, &[], &witness).unwrap();
81+
}

examples/acc-powers.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use binius_circuits::builder::ConstraintSystemBuilder;
2+
use binius_core::constraint_system::validate::validate_witness;
3+
use binius_field::{
4+
arch::OptimalUnderlier, BinaryField, BinaryField128b, BinaryField16b, BinaryField32b,
5+
PackedField,
6+
};
7+
8+
type U = OptimalUnderlier;
9+
type F128 = BinaryField128b;
10+
type F32 = BinaryField32b;
11+
type F16 = BinaryField16b;
12+
13+
const LOG_SIZE: usize = 3;
14+
15+
// FIXME: Following gadgets are unconstrained. Only for demonstrative purpose, don't use in production
16+
17+
// Values for the Transparent columns are known to verifier, so they can be used for storing non-private data
18+
// (like constants for example). The following gadget demonstrates how to use Powers abstraction to build a
19+
// Transparent column that keeps following values (we write them during witness population):
20+
//
21+
// [ F32(x)^0, F32(x)^1 , F32(x)^2, ... F32(x)^(2^LOG_SIZE) ],
22+
23+
// where 'x' is a multiplicative generator - a public value that exists for every BinaryField
24+
//
25+
fn powers_gadget_f32(builder: &mut ConstraintSystemBuilder<U, F128>, name: impl ToString) {
26+
builder.push_namespace(name);
27+
28+
let generator = F32::MULTIPLICATIVE_GENERATOR;
29+
let powers = binius_core::transparent::powers::Powers::new(LOG_SIZE, generator.into());
30+
let transparent = builder
31+
.add_transparent("Powers of F32 gen", powers)
32+
.unwrap();
33+
34+
if let Some(witness) = builder.witness() {
35+
let mut transparent_witness = witness.new_column::<F32>(transparent);
36+
let transparent_values = transparent_witness.as_mut_slice::<F32>();
37+
for (exp, val) in transparent_values.iter_mut().enumerate() {
38+
*val = generator.pow(exp as u64);
39+
}
40+
}
41+
42+
builder.pop_namespace();
43+
}
44+
45+
// Only Field is being changed
46+
fn powers_gadget_f16(builder: &mut ConstraintSystemBuilder<U, F128>, name: impl ToString) {
47+
builder.push_namespace(name);
48+
49+
let generator = F16::MULTIPLICATIVE_GENERATOR;
50+
let powers = binius_core::transparent::powers::Powers::new(LOG_SIZE, generator.into());
51+
let transparent = builder
52+
.add_transparent("Powers of F16 gen", powers)
53+
.unwrap();
54+
55+
if let Some(witness) = builder.witness() {
56+
let mut transparent_witness = witness.new_column::<F16>(transparent);
57+
let transparent_values = transparent_witness.as_mut_slice::<F16>();
58+
for (exp, val) in transparent_values.iter_mut().enumerate() {
59+
*val = generator.pow(exp as u64);
60+
}
61+
}
62+
63+
builder.pop_namespace();
64+
}
65+
66+
fn main() {
67+
let allocator = bumpalo::Bump::new();
68+
let mut builder = ConstraintSystemBuilder::<U, F128>::new_with_witness(&allocator);
69+
70+
powers_gadget_f16(&mut builder, "f16");
71+
powers_gadget_f32(&mut builder, "f32");
72+
73+
let witness = builder.take_witness().unwrap();
74+
let constraints_system = builder.build().unwrap();
75+
76+
validate_witness(&constraints_system, &[], &witness).unwrap();
77+
}

0 commit comments

Comments
 (0)